Merge "Add Intent.ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD for privacy hub" into sc-dev
diff --git a/Android.bp b/Android.bp
index 6a47db1..4f0559b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -424,6 +424,7 @@
":libbluetooth-binder-aidl",
":libcamera_client_aidl",
":libcamera_client_framework_aidl",
+ ":packagemanager_aidl",
":libupdate_engine_aidl",
":resourcemanager_aidl",
":storaged_aidl",
@@ -465,6 +466,7 @@
static_libs: [
"android.net.ipsec.ike.stubs.module_lib",
"framework-appsearch.stubs.module_lib",
+ "framework-connectivity.stubs.module_lib",
"framework-graphics.stubs.module_lib",
"framework-media.stubs.module_lib",
"framework-mediaprovider.stubs.module_lib",
@@ -487,6 +489,7 @@
"android.net.ipsec.ike.impl",
"framework-minus-apex",
"framework-appsearch.impl",
+ "framework-connectivity.impl",
"framework-graphics.impl",
"framework-mediaprovider.impl",
"framework-permission.impl",
@@ -507,15 +510,6 @@
],
}
-filegroup {
- name: "framework-all-sources",
- srcs: [
- ":framework-mime-sources",
- ":framework-non-updatable-sources",
- ":framework-updatable-sources",
- ],
-}
-
// AIDL files under these paths are mixture of public and private ones.
// They shouldn't be exported across module boundaries.
java_defaults {
@@ -643,14 +637,17 @@
defaults: ["framework-aidl-export-defaults"],
srcs: [
":framework-non-updatable-sources",
- ":framework-connectivity-sources",
"core/java/**/*.logtags",
],
// See comment on framework-atb-backward-compatibility module below
exclude_srcs: ["core/java/android/content/pm/AndroidTestBaseUpdater.java"],
aidl: {
generate_get_transaction_name: true,
- local_include_dirs: ["media/aidl"],
+ local_include_dirs: [
+ "media/aidl",
+ // TODO: move to include_dirs when migrated to packages/modules
+ "packages/Connectivity/framework/aidl-export",
+ ],
include_dirs: ["frameworks/av/aidl"],
},
dxflags: [
@@ -703,8 +700,6 @@
apex_available: ["//apex_available:platform"],
visibility: [
"//frameworks/base",
- // TODO: remove when framework-connectivity can build against API
- "//frameworks/base/packages/Connectivity/framework",
// TODO(b/147128803) remove the below lines
"//frameworks/base/apex/appsearch/framework",
"//frameworks/base/apex/blobstore/framework",
@@ -743,6 +738,7 @@
static_libs: [
"app-compat-annotations",
"framework-minus-apex",
+ "framework-connectivity.impl", // TODO(b/182859030): should be removed
"framework-appsearch.impl", // TODO(b/146218515): should be removed
"framework-updatable-stubs-module_libs_api",
],
@@ -854,10 +850,14 @@
srcs: [
"core/java/android/annotation/AnyThread.java",
"core/java/android/annotation/AppIdInt.java",
- "core/java/android/annotation/CallSuper.java",
+ "core/java/android/annotation/BytesLong.java",
"core/java/android/annotation/CallbackExecutor.java",
+ "core/java/android/annotation/CallSuper.java",
"core/java/android/annotation/CheckResult.java",
"core/java/android/annotation/CurrentTimeMillisLong.java",
+ "core/java/android/annotation/CurrentTimeSecondsLong.java",
+ "core/java/android/annotation/DrawableRes.java",
+ "core/java/android/annotation/DurationMillisLong.java",
"core/java/android/annotation/Hide.java",
"core/java/android/annotation/IntDef.java",
"core/java/android/annotation/IntRange.java",
@@ -875,8 +875,8 @@
"core/java/android/annotation/UserIdInt.java",
"core/java/android/annotation/WorkerThread.java",
"core/java/com/android/internal/annotations/GuardedBy.java",
- "core/java/com/android/internal/annotations/VisibleForTesting.java",
"core/java/com/android/internal/annotations/Immutable.java",
+ "core/java/com/android/internal/annotations/VisibleForTesting.java",
],
}
@@ -890,7 +890,6 @@
name: "framework-ike-shared-srcs",
visibility: ["//packages/modules/IPsec"],
srcs: [
- "core/java/android/annotation/StringDef.java",
"core/java/android/net/annotations/PolicyDirection.java",
"core/java/com/android/internal/util/HexDump.java",
"core/java/com/android/internal/util/IState.java",
@@ -1364,36 +1363,6 @@
],
}
-filegroup {
- name: "framework-media-annotation-srcs",
- srcs: [
- ":framework-annotations",
- "core/java/android/annotation/CallbackExecutor.java",
- "core/java/android/annotation/CallSuper.java",
- "core/java/android/annotation/DrawableRes.java",
- "core/java/android/annotation/LongDef.java",
- "core/java/android/annotation/StringDef.java",
- ],
-}
-
-filegroup {
- name: "framework-mediaprovider-annotation-sources",
- srcs: [
- ":framework-annotations",
- "core/java/android/annotation/BytesLong.java",
- "core/java/android/annotation/CurrentTimeSecondsLong.java",
- "core/java/android/annotation/DurationMillisLong.java",
- ],
-}
-
-// Creates an index of AIDL methods; used for adding UnsupportedAppUsage
-// annotations to private apis
-aidl_mapping {
- name: "framework-aidl-mappings",
- srcs: [":framework-all-sources"],
- output: "framework-aidl-mappings.txt",
-}
-
// Avoid including Parcelable classes as we don't want to have two copies of
// Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony)
// and TeleService app (packages/services/Telephony).
@@ -1461,7 +1430,7 @@
],
libs: [
"framework-annotations-lib",
- "framework-connectivity",
+ "framework-connectivity.stubs.module_lib",
"unsupportedappusage",
],
visibility: [
diff --git a/StubLibraries.bp b/StubLibraries.bp
index c61f7c6..9b43749 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -449,6 +449,7 @@
"core/java/android/os/RemoteException.java",
"core/java/android/util/AndroidException.java",
],
+ libs: ["framework-annotations-lib"],
installable: false,
sdk_version: "core_platform",
annotations_enabled: true,
@@ -462,7 +463,7 @@
java_library_static {
name: "hwbinder.stubs",
sdk_version: "core_current",
- libs: ["stub-annotations"],
+ libs: ["framework-annotations-lib"],
srcs: [
":hwbinder-stubs-docs",
],
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 47cf17c..5acfe6a 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -26,10 +26,8 @@
}
public static final class AppSearchManager.SearchContext.Builder {
- ctor @Deprecated public AppSearchManager.SearchContext.Builder();
ctor public AppSearchManager.SearchContext.Builder(@NonNull String);
method @NonNull public android.app.appsearch.AppSearchManager.SearchContext build();
- method @Deprecated @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String);
}
public final class AppSearchResult<ValueType> {
@@ -53,7 +51,6 @@
public final class AppSearchSchema {
method @NonNull public java.util.List<android.app.appsearch.AppSearchSchema.PropertyConfig> getProperties();
method @NonNull public String getSchemaType();
- method @Deprecated @IntRange(from=0) public int getVersion();
}
public static final class AppSearchSchema.BooleanPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
@@ -69,7 +66,6 @@
ctor public AppSearchSchema.Builder(@NonNull String);
method @NonNull public android.app.appsearch.AppSearchSchema.Builder addProperty(@NonNull android.app.appsearch.AppSearchSchema.PropertyConfig);
method @NonNull public android.app.appsearch.AppSearchSchema build();
- method @Deprecated @NonNull public android.app.appsearch.AppSearchSchema.Builder setVersion(@IntRange(from=0) int);
}
public static final class AppSearchSchema.BytesPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
@@ -149,7 +145,6 @@
method public void remove(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
method public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec);
- method @Deprecated public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>);
method public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>);
}
@@ -181,15 +176,12 @@
method public int getScore();
method public long getTtlMillis();
method @NonNull public String getUri();
- field @Deprecated public static final String DEFAULT_NAMESPACE = "";
}
public static class GenericDocument.Builder<BuilderType extends android.app.appsearch.GenericDocument.Builder> {
- ctor @Deprecated public GenericDocument.Builder(@NonNull String, @NonNull String);
ctor public GenericDocument.Builder(@NonNull String, @NonNull String, @NonNull String);
method @NonNull public android.app.appsearch.GenericDocument build();
method @NonNull public BuilderType setCreationTimestampMillis(long);
- method @Deprecated @NonNull public BuilderType setNamespace(@NonNull String);
method @NonNull public BuilderType setPropertyBoolean(@NonNull String, @NonNull boolean...);
method @NonNull public BuilderType setPropertyBytes(@NonNull String, @NonNull byte[]...);
method @NonNull public BuilderType setPropertyDocument(@NonNull String, @NonNull android.app.appsearch.GenericDocument...);
@@ -208,16 +200,14 @@
}
public static final class GetByUriRequest.Builder {
- ctor @Deprecated public GetByUriRequest.Builder();
ctor public GetByUriRequest.Builder(@NonNull String);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.GetByUriRequest build();
- method @Deprecated @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
}
- public class GetSchemaResponse extends java.util.HashSet<android.app.appsearch.AppSearchSchema> {
+ public class GetSchemaResponse {
method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
method @IntRange(from=0) public int getVersion();
}
@@ -265,12 +255,10 @@
}
public static final class RemoveByUriRequest.Builder {
- ctor @Deprecated public RemoveByUriRequest.Builder();
ctor public RemoveByUriRequest.Builder(@NonNull String);
method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.RemoveByUriRequest build();
- method @Deprecated @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
}
public final class ReportSystemUsageRequest {
@@ -295,17 +283,14 @@
}
public static final class ReportUsageRequest.Builder {
- ctor @Deprecated public ReportUsageRequest.Builder();
ctor public ReportUsageRequest.Builder(@NonNull String);
method @NonNull public android.app.appsearch.ReportUsageRequest build();
- method @Deprecated @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String);
method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUri(@NonNull String);
method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimeMillis(long);
}
public final class SearchResult {
method @NonNull public String getDatabaseName();
- method @Deprecated @NonNull public android.app.appsearch.GenericDocument getDocument();
method @NonNull public android.app.appsearch.GenericDocument getGenericDocument();
method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
method @NonNull public String getPackageName();
@@ -322,12 +307,10 @@
public static final class SearchResult.MatchInfo {
method @NonNull public CharSequence getExactMatch();
- method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition();
method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchRange();
method @NonNull public String getFullText();
method @NonNull public String getPropertyPath();
method @NonNull public CharSequence getSnippet();
- method @Deprecated @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition();
method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetRange();
}
@@ -405,7 +388,6 @@
method @NonNull public java.util.Map<java.lang.String,android.app.appsearch.Migrator> getMigrators();
method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
method @NonNull public java.util.Set<java.lang.String> getSchemasNotDisplayedBySystem();
- method @Deprecated @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi();
method @NonNull public java.util.Map<java.lang.String,java.util.Set<android.app.appsearch.PackageIdentifier>> getSchemasVisibleToPackages();
method @IntRange(from=1) public int getVersion();
method public boolean isForceOverride();
@@ -421,7 +403,6 @@
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrators(@NonNull java.util.Map<java.lang.String,android.app.appsearch.Migrator>);
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(@NonNull String, boolean);
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier);
- method @Deprecated @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean);
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setVersion(@IntRange(from=1) int);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 0c6b86b..9776827 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -147,22 +147,10 @@
/** Builder for {@link SearchContext} objects. */
public static final class Builder {
- private String mDatabaseName;
+ private final String mDatabaseName;
private boolean mBuilt = false;
/**
- * TODO(b/181887768): This method exists only for dogfooder transition and must be
- * removed.
- *
- * @deprecated Please supply the databaseName in {@link #Builder(String)} instead. This
- * method exists only for dogfooder transition and must be removed.
- */
- @Deprecated
- public Builder() {
- mDatabaseName = "";
- }
-
- /**
* Creates a new {@link SearchContext.Builder}.
*
* <p>{@link AppSearchSession} will create or open a database under the given name.
@@ -182,37 +170,6 @@
mDatabaseName = databaseName;
}
- /**
- * Sets the name of the database associated with {@link AppSearchSession}.
- *
- * <p>{@link AppSearchSession} will create or open a database under the given name.
- *
- * <p>Databases with different names are fully separate with distinct types, namespaces,
- * and data.
- *
- * <p>Database name cannot contain {@code '/'}.
- *
- * <p>If not specified, defaults to the empty string.
- *
- * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
- * removed.
- *
- * @param databaseName The name of the database.
- * @throws IllegalArgumentException if the databaseName contains {@code '/'}.
- * @deprecated Please supply the databaseName in {@link #Builder(String)} instead. This
- * method exists only for dogfooder transition and must be removed.
- */
- @Deprecated
- @NonNull
- public Builder setDatabaseName(@NonNull String databaseName) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- Objects.requireNonNull(databaseName);
- Preconditions.checkArgument(
- !databaseName.contains("/"), "Database name cannot contain '/'");
- mDatabaseName = databaseName;
- return this;
- }
-
/** Builds a {@link SearchContext} instance. */
@NonNull
public SearchContext build() {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
new file mode 100644
index 0000000..e585d91
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * The helper class for {@link AppSearchSchema} migration.
+ *
+ * <p>It will query and migrate {@link GenericDocument} in given type to a new version.
+ * @hide
+ */
+public class AppSearchMigrationHelper implements Closeable {
+ private final IAppSearchManager mService;
+ private final String mPackageName;
+ private final String mDatabaseName;
+ private final int mUserId;
+ private final File mMigratedFile;
+ private final Map<String, Integer> mCurrentVersionMap;
+ private final Map<String, Integer> mFinalVersionMap;
+ private boolean mAreDocumentsMigrated = false;
+
+ AppSearchMigrationHelper(@NonNull IAppSearchManager service,
+ @UserIdInt int userId,
+ @NonNull Map<String, Integer> currentVersionMap,
+ @NonNull Map<String, Integer> finalVersionMap,
+ @NonNull String packageName,
+ @NonNull String databaseName) throws IOException {
+ mService = Objects.requireNonNull(service);
+ mCurrentVersionMap = Objects.requireNonNull(currentVersionMap);
+ mFinalVersionMap = Objects.requireNonNull(finalVersionMap);
+ mPackageName = Objects.requireNonNull(packageName);
+ mDatabaseName = Objects.requireNonNull(databaseName);
+ mUserId = userId;
+ mMigratedFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
+ }
+
+ /**
+ * Queries all documents that need to be migrated to a different version and transform
+ * documents to that version by passing them to the provided {@link Migrator}.
+ *
+ * <p>The method will be executed on the executor provided to
+ * {@link AppSearchSession#setSchema}.
+ *
+ * @param schemaType The schema type that needs to be updated and whose {@link GenericDocument}
+ * need to be migrated.
+ * @param migrator The {@link Migrator} that will upgrade or downgrade a {@link
+ * GenericDocument} to new version.
+ */
+ @WorkerThread
+ public void queryAndTransform(@NonNull String schemaType, @NonNull Migrator migrator)
+ throws IOException, AppSearchException, InterruptedException, ExecutionException {
+ File queryFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
+ try (ParcelFileDescriptor fileDescriptor =
+ ParcelFileDescriptor.open(queryFile, MODE_WRITE_ONLY)) {
+ AndroidFuture<AppSearchResult<Void>> androidFuture = new AndroidFuture<>();
+ mService.writeQueryResultsToFile(mPackageName, mDatabaseName,
+ fileDescriptor,
+ /*queryExpression=*/ "",
+ new SearchSpec.Builder()
+ .addFilterSchemas(schemaType)
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build().getBundle(),
+ mUserId,
+ new IAppSearchResultCallback.Stub() {
+ @Override
+ public void onResult(AppSearchResult result) throws RemoteException {
+ androidFuture.complete(result);
+ }
+ });
+ AppSearchResult<Void> result = androidFuture.get();
+ if (!result.isSuccess()) {
+ throw new AppSearchException(result.getResultCode(), result.getErrorMessage());
+ }
+ readAndTransform(queryFile, migrator);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ queryFile.delete();
+ }
+ }
+
+ /**
+ * Puts all {@link GenericDocument} migrated from the previous call to
+ * {@link #queryAndTransform} into AppSearch.
+ *
+ * <p> This method should be only called once.
+ *
+ * @param responseBuilder a SetSchemaResponse builder whose result will be returned by this
+ * function with any
+ * {@link android.app.appsearch.SetSchemaResponse.MigrationFailure}
+ * added in.
+ * @return the {@link SetSchemaResponse} for {@link AppSearchSession#setSchema} call.
+ */
+ @NonNull
+ AppSearchResult<SetSchemaResponse> putMigratedDocuments(
+ @NonNull SetSchemaResponse.Builder responseBuilder) {
+ if (!mAreDocumentsMigrated) {
+ return AppSearchResult.newSuccessfulResult(responseBuilder.build());
+ }
+ try (ParcelFileDescriptor fileDescriptor =
+ ParcelFileDescriptor.open(mMigratedFile, MODE_READ_ONLY)) {
+ AndroidFuture<AppSearchResult<List<Bundle>>> androidFuture = new AndroidFuture<>();
+ mService.putDocumentsFromFile(mPackageName, mDatabaseName, fileDescriptor, mUserId,
+ new IAppSearchResultCallback.Stub() {
+ @Override
+ public void onResult(AppSearchResult result) throws RemoteException {
+ androidFuture.complete(result);
+ }
+ });
+ AppSearchResult<List<Bundle>> result = androidFuture.get();
+ if (!result.isSuccess()) {
+ return AppSearchResult.newFailedResult(result);
+ }
+ List<Bundle> migratedFailureBundles = result.getResultValue();
+ for (int i = 0; i < migratedFailureBundles.size(); i++) {
+ responseBuilder.addMigrationFailure(
+ new SetSchemaResponse.MigrationFailure(migratedFailureBundles.get(i)));
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (Throwable t) {
+ return AppSearchResult.throwableToFailedResult(t);
+ } finally {
+ mMigratedFile.delete();
+ }
+ return AppSearchResult.newSuccessfulResult(responseBuilder.build());
+ }
+
+ /**
+ * Reads all saved {@link GenericDocument}s from the given {@link File}.
+ *
+ * <p>Transforms those {@link GenericDocument}s to the final version.
+ *
+ * <p>Save migrated {@link GenericDocument}s to the {@link #mMigratedFile}.
+ */
+ private void readAndTransform(@NonNull File file, @NonNull Migrator migrator)
+ throws IOException {
+ try (DataInputStream inputStream = new DataInputStream(new FileInputStream(file));
+ DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(
+ mMigratedFile, /*append=*/ true))) {
+ GenericDocument document;
+ while (true) {
+ try {
+ document = readDocumentFromInputStream(inputStream);
+ } catch (EOFException e) {
+ break;
+ // Nothing wrong. We just finished reading.
+ }
+
+ int currentVersion = mCurrentVersionMap.get(document.getSchemaType());
+ int finalVersion = mFinalVersionMap.get(document.getSchemaType());
+
+ GenericDocument newDocument;
+ if (currentVersion < finalVersion) {
+ newDocument = migrator.onUpgrade(currentVersion, finalVersion, document);
+ } else {
+ // currentVersion == finalVersion case won't trigger migration and get here.
+ newDocument = migrator.onDowngrade(currentVersion, finalVersion, document);
+ }
+ writeBundleToOutputStream(outputStream, newDocument.getBundle());
+ }
+ mAreDocumentsMigrated = true;
+ }
+ }
+
+ /**
+ * Reads the {@link Bundle} of a {@link GenericDocument} from given {@link DataInputStream}.
+ *
+ * @param inputStream The inputStream to read from
+ *
+ * @throws IOException on read failure.
+ * @throws EOFException if {@link java.io.InputStream} reaches the end.
+ */
+ @NonNull
+ public static GenericDocument readDocumentFromInputStream(
+ @NonNull DataInputStream inputStream) throws IOException {
+ int length = inputStream.readInt();
+ if (length == 0) {
+ throw new EOFException();
+ }
+ byte[] serializedMessage = new byte[length];
+ inputStream.read(serializedMessage);
+
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.unmarshall(serializedMessage, 0, serializedMessage.length);
+ parcel.setDataPosition(0);
+ Bundle bundle = parcel.readBundle();
+ return new GenericDocument(bundle);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ /**
+ * Serializes a {@link Bundle} and writes into the given {@link DataOutputStream}.
+ */
+ public static void writeBundleToOutputStream(
+ @NonNull DataOutputStream outputStream, @NonNull Bundle bundle)
+ throws IOException {
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeBundle(bundle);
+ byte[] serializedMessage = parcel.marshall();
+ outputStream.writeInt(serializedMessage.length);
+ outputStream.write(serializedMessage);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ mMigratedFile.delete();
+ }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index bf733ed..4dd1b79 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -19,6 +19,8 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.app.appsearch.util.SchemaMigrationUtil;
import android.os.Bundle;
import android.os.ParcelableException;
import android.os.RemoteException;
@@ -26,6 +28,7 @@
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.Preconditions;
import java.io.Closeable;
@@ -55,7 +58,6 @@
private boolean mIsMutated = false;
private boolean mIsClosed = false;
-
/**
* Creates a search session for the client, defined by the {@code userId} and
* {@code packageName}.
@@ -104,18 +106,6 @@
}
/**
- * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
- * @deprecated This method exists only for dogfooder transition and must be removed.
- */
- @Deprecated
- public void setSchema(
- @NonNull SetSchemaRequest request,
- @NonNull @CallbackExecutor Executor callbackExecutor,
- @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
- setSchema(request, callbackExecutor, callbackExecutor, callback);
- }
-
- /**
* Sets the schema that represents the organizational structure of data within the AppSearch
* database.
*
@@ -157,36 +147,25 @@
}
schemasPackageAccessibleBundles.put(entry.getKey(), packageIdentifierBundles);
}
- try {
- mService.setSchema(
- mPackageName,
- mDatabaseName,
+
+ // No need to trigger migration if user never set migrator
+ if (request.getMigrators().isEmpty()) {
+ setSchemaNoMigrations(
+ request,
schemaBundles,
- new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
schemasPackageAccessibleBundles,
- request.isForceOverride(),
- mUserId,
- request.getVersion(),
- new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- callbackExecutor.execute(() -> {
- if (result.isSuccess()) {
- callback.accept(
- // TODO(b/177266929) implement Migration in platform.
- // TODO(b/183177268): once migration is implemented, run
- // it on workExecutor.
- AppSearchResult.newSuccessfulResult(
- new SetSchemaResponse.Builder().build()));
- } else {
- callback.accept(result);
- }
- });
- }
- });
- mIsMutated = true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ callbackExecutor,
+ callback);
+ } else {
+ setSchemaWithMigrations(
+ request,
+ schemaBundles,
+ schemasPackageAccessibleBundles,
+ workExecutor,
+ callbackExecutor,
+ callback);
}
+ mIsMutated = true;
}
/**
@@ -608,8 +587,28 @@
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
- // TODO(b/182909475): Implement getStorageInfo
- throw new UnsupportedOperationException();
+ try {
+ mService.getStorageInfo(
+ mPackageName,
+ mDatabaseName,
+ mUserId,
+ new IAppSearchResultCallback.Stub() {
+ public void onResult(AppSearchResult result) {
+ executor.execute(() -> {
+ if (result.isSuccess()) {
+ Bundle responseBundle = (Bundle) result.getResultValue();
+ StorageInfo response =
+ new StorageInfo(responseBundle);
+ callback.accept(AppSearchResult.newSuccessfulResult(response));
+ } else {
+ callback.accept(result);
+ }
+ });
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -627,4 +626,217 @@
}
}
}
+
+ /**
+ * Set schema to Icing for no-migration scenario.
+ *
+ * <p>We only need one time {@link #setSchema} call for no-migration scenario by using the
+ * forceoverride in the request.
+ */
+ private void setSchemaNoMigrations(
+ @NonNull SetSchemaRequest request,
+ @NonNull List<Bundle> schemaBundles,
+ @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
+ try {
+ mService.setSchema(
+ mPackageName,
+ mDatabaseName,
+ schemaBundles,
+ new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
+ schemasPackageAccessibleBundles,
+ request.isForceOverride(),
+ mUserId,
+ request.getVersion(),
+ new IAppSearchResultCallback.Stub() {
+ public void onResult(AppSearchResult result) {
+ executor.execute(() -> {
+ if (result.isSuccess()) {
+ try {
+ SetSchemaResponse setSchemaResponse =
+ new SetSchemaResponse(
+ (Bundle) result.getResultValue());
+ if (!request.isForceOverride()) {
+ // Throw exception if there is any deleted types or
+ // incompatible types. That's the only case we swallowed
+ // in the AppSearchImpl#setSchema().
+ checkDeletedAndIncompatible(
+ setSchemaResponse.getDeletedTypes(),
+ setSchemaResponse.getIncompatibleTypes());
+ }
+ callback.accept(AppSearchResult
+ .newSuccessfulResult(setSchemaResponse));
+ } catch (Throwable t) {
+ callback.accept(AppSearchResult.throwableToFailedResult(t));
+ }
+ } else {
+ callback.accept(result);
+ }
+ });
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set schema to Icing for migration scenario.
+ *
+ * <p>First time {@link #setSchema} call with forceOverride is false gives us all incompatible
+ * changes. After trigger migrations, the second time call {@link #setSchema} will actually
+ * apply the changes.
+ */
+ private void setSchemaWithMigrations(
+ @NonNull SetSchemaRequest request,
+ @NonNull List<Bundle> schemaBundles,
+ @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+ @NonNull Executor workExecutor,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
+ workExecutor.execute(() -> {
+ try {
+ // Migration process
+ // 1. Generate the current and the final version map.
+ AndroidFuture<AppSearchResult<GetSchemaResponse>> getSchemaFuture =
+ new AndroidFuture<>();
+ getSchema(callbackExecutor, getSchemaFuture::complete);
+ AppSearchResult<GetSchemaResponse> getSchemaResult = getSchemaFuture.get();
+ if (!getSchemaResult.isSuccess()) {
+ callbackExecutor.execute(() ->
+ callback.accept(AppSearchResult.newFailedResult(getSchemaResult)));
+ return;
+ }
+ GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue();
+ Set<AppSearchSchema> currentSchemas = getSchemaResponse.getSchemas();
+ Map<String, Integer> currentVersionMap = SchemaMigrationUtil.buildVersionMap(
+ currentSchemas, getSchemaResponse.getVersion());
+ Map<String, Integer> finalVersionMap = SchemaMigrationUtil.buildVersionMap(
+ request.getSchemas(), request.getVersion());
+
+ // 2. SetSchema with forceOverride=false, to retrieve the list of
+ // incompatible/deleted types.
+ AndroidFuture<AppSearchResult<Bundle>> setSchemaFuture = new AndroidFuture<>();
+ mService.setSchema(
+ mPackageName,
+ mDatabaseName,
+ schemaBundles,
+ new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
+ schemasPackageAccessibleBundles,
+ /*forceOverride=*/ false,
+ mUserId,
+ request.getVersion(),
+ new IAppSearchResultCallback.Stub() {
+ public void onResult(AppSearchResult result) {
+ setSchemaFuture.complete(result);
+ }
+ });
+ AppSearchResult<Bundle> setSchemaResult = setSchemaFuture.get();
+ if (!setSchemaResult.isSuccess()) {
+ callbackExecutor.execute(() ->
+ callback.accept(AppSearchResult.newFailedResult(setSchemaResult)));
+ return;
+ }
+ SetSchemaResponse setSchemaResponse =
+ new SetSchemaResponse(setSchemaResult.getResultValue());
+
+ // 1. If forceOverride is false, check that all incompatible types will be migrated.
+ // If some aren't we must throw an error, rather than proceeding and deleting those
+ // types.
+ if (!request.isForceOverride()) {
+ Set<String> unmigratedTypes =
+ SchemaMigrationUtil.getUnmigratedIncompatibleTypes(
+ setSchemaResponse.getIncompatibleTypes(),
+ request.getMigrators(),
+ currentVersionMap,
+ finalVersionMap);
+
+ // check if there are any unmigrated types or deleted types. If there are, we
+ // will throw an exception.
+ // Since the force override is false, the schema will not have been set if there
+ // are any incompatible or deleted types.
+ checkDeletedAndIncompatible(
+ setSchemaResponse.getDeletedTypes(), unmigratedTypes);
+ }
+
+ try (AppSearchMigrationHelper migrationHelper =
+ new AppSearchMigrationHelper(
+ mService, mUserId, currentVersionMap, finalVersionMap,
+ mPackageName, mDatabaseName)) {
+ Map<String, Migrator> migratorMap = request.getMigrators();
+
+ // 2. Trigger migration for all migrators.
+ // TODO(b/177266929) trigger migration for all types together rather than
+ // separately.
+ Set<String> migratedTypes = new ArraySet<>();
+ for (Map.Entry<String, Migrator> entry : migratorMap.entrySet()) {
+ String schemaType = entry.getKey();
+ Migrator migrator = entry.getValue();
+ if (SchemaMigrationUtil.shouldTriggerMigration(
+ schemaType, migrator, currentVersionMap, finalVersionMap)) {
+ migrationHelper.queryAndTransform(schemaType, migrator);
+ migratedTypes.add(schemaType);
+ }
+ }
+
+ // 3. SetSchema a second time with forceOverride=true if the first attempted
+ // failed.
+ if (!setSchemaResponse.getIncompatibleTypes().isEmpty()
+ || !setSchemaResponse.getDeletedTypes().isEmpty()) {
+ AndroidFuture<AppSearchResult<Bundle>> setSchema2Future =
+ new AndroidFuture<>();
+ // only trigger second setSchema() call if the first one is fail.
+ mService.setSchema(
+ mPackageName,
+ mDatabaseName,
+ schemaBundles,
+ new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
+ schemasPackageAccessibleBundles,
+ /*forceOverride=*/ true,
+ request.getVersion(),
+ mUserId,
+ new IAppSearchResultCallback.Stub() {
+ @Override
+ public void onResult(AppSearchResult result) {
+ setSchema2Future.complete(result);
+ }
+ });
+ AppSearchResult<Bundle> setSchema2Result = setSchema2Future.get();
+ if (!setSchema2Result.isSuccess()) {
+ // we failed to set the schema in second time with forceOverride = true,
+ // which is an impossible case. Since we only swallow the incompatible
+ // error in the first setSchema call, all other errors will be thrown at
+ // the first time.
+ callbackExecutor.execute(() -> callback.accept(
+ AppSearchResult.newFailedResult(setSchemaResult)));
+ return;
+ }
+ }
+
+ SetSchemaResponse.Builder responseBuilder = setSchemaResponse.toBuilder()
+ .addMigratedTypes(migratedTypes);
+ AppSearchResult<SetSchemaResponse> putResult =
+ migrationHelper.putMigratedDocuments(responseBuilder);
+ callbackExecutor.execute(() -> callback.accept(putResult));
+ }
+ } catch (Throwable t) {
+ callbackExecutor.execute(() -> callback.accept(
+ AppSearchResult.throwableToFailedResult(t)));
+ }
+ });
+ }
+
+ /** Checks the setSchema() call won't delete any types or has incompatible types. */
+ //TODO(b/177266929) move this method to util
+ private void checkDeletedAndIncompatible(Set<String> deletedTypes,
+ Set<String> incompatibleTypes)
+ throws AppSearchException {
+ if (!deletedTypes.isEmpty() || !incompatibleTypes.isEmpty()) {
+ String newMessage = "Schema is incompatible."
+ + "\n Deleted types: " + deletedTypes
+ + "\n Incompatible types: " + incompatibleTypes;
+ throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage);
+ }
+ }
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index d436488..a8ac27c 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -21,6 +21,7 @@
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.IAppSearchBatchResultCallback;
import android.app.appsearch.IAppSearchResultCallback;
+import android.os.ParcelFileDescriptor;
import com.android.internal.infra.AndroidFuture;
parcelable SearchResults;
@@ -41,7 +42,8 @@
* incompatible documents will be deleted.
* @param userId Id of the calling user
* @param callback {@link IAppSearchResultCallback#onResult} will be called with an
- * {@link AppSearchResult}<{@link Void}>.
+ * {@link AppSearchResult}<{@link Bundle}>, where the value are
+ * {@link SetSchemaResponse} bundle.
*/
void setSchema(
in String packageName,
@@ -189,6 +191,47 @@
void invalidateNextPageToken(in long nextPageToken, in int userId);
/**
+ * Searches a document based on a given specifications.
+ *
+ * <p>Documents will be save to the given ParcelFileDescriptor
+ *
+ * @param packageName The name of the package to query over.
+ * @param databaseName The databaseName this query for.
+ * @param fileDescriptor The ParcelFileDescriptor where documents should be written to.
+ * @param queryExpression String to search for.
+ * @param searchSpecBundle SearchSpec bundle.
+ * @param userId Id of the calling user.
+ * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
+ * {@link AppSearchResult}<{@code null}>.
+ */
+ void writeQueryResultsToFile(
+ in String packageName,
+ in String databaseName,
+ in ParcelFileDescriptor fileDescriptor,
+ in String queryExpression,
+ in Bundle searchSpecBundle,
+ in int userId,
+ in IAppSearchResultCallback callback);
+
+ /**
+ * Inserts documents from the given file into the index.
+ *
+ * @param packageName The name of the package that owns this document.
+ * @param databaseName The name of the database where this document lives.
+ * @param fileDescriptor The ParcelFileDescriptor where documents should be read from.
+ * @param userId Id of the calling user.
+ * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
+ * {@link AppSearchResult}<{@link List}<{@link Bundle}>>, where the value are
+ * MigrationFailure bundles.
+ */
+ void putDocumentsFromFile(
+ in String packageName,
+ in String databaseName,
+ in ParcelFileDescriptor fileDescriptor,
+ in int userId,
+ in IAppSearchResultCallback callback);
+
+ /**
* Reports usage of a particular document by URI and namespace.
*
* <p>A usage report represents an event in which a user interacted with or viewed a document.
@@ -264,6 +307,22 @@
in IAppSearchResultCallback callback);
/**
+ * Gets the storage info.
+ *
+ * @param packageName The name of the package to get the storage info for.
+ * @param databaseName The databaseName to get the storage info for.
+ * @param userId Id of the calling user
+ * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
+ * {@link AppSearchResult}<{@link Bundle}>, where the value is a
+ * {@link StorageInfo}.
+ */
+ void getStorageInfo(
+ in String packageName,
+ in String databaseName,
+ in int userId,
+ in IAppSearchResultCallback callback);
+
+ /**
* Persists all update/delete requests to the disk.
*
* @param userId Id of the calling user
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 79b7b75..a8048dc 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -17,7 +17,6 @@
package android.app.appsearch;
import android.annotation.IntDef;
-import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.appsearch.exceptions.IllegalSchemaException;
@@ -77,12 +76,6 @@
return mBundle.getString(SCHEMA_TYPE_FIELD, "");
}
- /** @deprecated Use {@link GetSchemaResponse#getVersion()} instead. */
- @Deprecated
- public @IntRange(from = 0) int getVersion() {
- return 0;
- }
-
/**
* Returns the list of {@link PropertyConfig}s that are part of this schema.
*
@@ -150,17 +143,6 @@
}
/**
- * @deprecated TODO(b/181887768): This method is a no-op and only exists for dogfooder
- * transition.
- */
- @Deprecated
- @NonNull
- public AppSearchSchema.Builder setVersion(@IntRange(from = 0) int version) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- return this;
- }
-
- /**
* Constructs a new {@link AppSearchSchema} from the contents of this builder.
*
* <p>After calling this method, the builder must no longer be used.
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 4ce95ea..8c9d950 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -46,15 +46,6 @@
public class GenericDocument {
private static final String TAG = "AppSearchGenericDocumen";
- /**
- * The default empty namespace.
- *
- * <p>TODO(b/181887768): This exists only for dogfooder transition and must be removed.
- *
- * @deprecated This exists only for dogfooder transition and must be removed.
- */
- @Deprecated public static final String DEFAULT_NAMESPACE = "";
-
/** The maximum number of elements in a repeatable field. */
private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
@@ -585,41 +576,6 @@
*
* <p>Once {@link #build} is called, the instance can no longer be used.
*
- * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
- * removed.
- *
- * @param uri the URI to set for the {@link GenericDocument}.
- * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The
- * provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema}
- * prior to inserting a document of this {@code schemaType} into the AppSearch index
- * using {@link AppSearchSession#put}. Otherwise, the document will be rejected by
- * {@link AppSearchSession#put} with result code {@link
- * AppSearchResult#RESULT_NOT_FOUND}.
- * @deprecated Please supply the namespace in {@link #Builder(String, String, String)}
- * instead. This method exists only for dogfooder transition and must be removed.
- */
- @Deprecated
- @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);
- }
-
- /**
- * Creates a new {@link GenericDocument.Builder}.
- *
- * <p>Once {@link #build} is called, the instance can no longer be used.
- *
* <p>URIs are unique within a namespace.
*
* <p>The number of namespaces per app should be kept small for efficiency reasons.
@@ -651,29 +607,6 @@
}
/**
- * Sets the app-defined namespace this document resides in. No special values are reserved
- * or understood by the infrastructure.
- *
- * <p>URIs are unique within a namespace.
- *
- * <p>The number of namespaces per app should be kept small for efficiency reasons.
- *
- * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
- * removed.
- *
- * @throws IllegalStateException if the builder has already been used.
- * @deprecated Please supply the namespace in {@link #Builder(String, String, String)}
- * instead. This method exists only for dogfooder transition and must be removed.
- */
- @Deprecated
- @NonNull
- public BuilderType setNamespace(@NonNull String namespace) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- 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
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index 6881a27..1719e14 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -107,49 +107,17 @@
* <p>Once {@link #build} is called, the instance can no longer be used.
*/
public static final class Builder {
- private String mNamespace;
+ private final String mNamespace;
private final Set<String> mUris = new ArraySet<>();
private final Map<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>();
private boolean mBuilt = false;
- /**
- * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
- *
- * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
- * exists only for dogfooder transition and must be removed.
- */
- @Deprecated
- public Builder() {
- mNamespace = GenericDocument.DEFAULT_NAMESPACE;
- }
-
/** Creates a {@link GetByUriRequest.Builder} instance. */
public Builder(@NonNull String namespace) {
mNamespace = Preconditions.checkNotNull(namespace);
}
/**
- * Sets the namespace to retrieve documents for.
- *
- * <p>If this is not called, the namespace defaults to an empty string.
- *
- * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
- * removed.
- *
- * @throws IllegalStateException if the builder has already been used.
- * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
- * exists only for dogfooder transition and must
- */
- @Deprecated
- @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.
*
* @throws IllegalStateException if the builder has already been used.
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java
index 3e69367..1f56ef3 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java
@@ -24,30 +24,17 @@
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.Set;
/** The response class of {@link AppSearchSession#getSchema} */
-// TODO(b/181887768) extends only for dogfooder transition. */
-public class GetSchemaResponse extends HashSet<AppSearchSchema> {
+public class GetSchemaResponse {
private static final String VERSION_FIELD = "version";
private static final String SCHEMAS_FIELD = "schemas";
private final Bundle mBundle;
- // TODO(b/181887768) Remove this method once this class no longer extends HashSet. */
- private static Set<AppSearchSchema> getSchemasFromBundle(Bundle bundle) {
- ArrayList<Bundle> schemaBundles = bundle.getParcelableArrayList(SCHEMAS_FIELD);
- Set<AppSearchSchema> schemas = new ArraySet<>(schemaBundles.size());
- for (int i = 0; i < schemaBundles.size(); i++) {
- schemas.add(new AppSearchSchema(schemaBundles.get(i)));
- }
- return schemas;
- }
-
GetSchemaResponse(@NonNull Bundle bundle) {
- super(getSchemasFromBundle(Preconditions.checkNotNull(bundle)));
- mBundle = bundle;
+ mBundle = Preconditions.checkNotNull(bundle);
}
/**
@@ -72,10 +59,17 @@
/**
* Return the schemas most recently successfully provided to {@link AppSearchSession#setSchema}.
+ *
+ * <p>It is inefficient to call this method repeatedly.
*/
@NonNull
public Set<AppSearchSchema> getSchemas() {
- return this;
+ ArrayList<Bundle> schemaBundles = mBundle.getParcelableArrayList(SCHEMAS_FIELD);
+ Set<AppSearchSchema> schemas = new ArraySet<>(schemaBundles.size());
+ for (int i = 0; i < schemaBundles.size(); i++) {
+ schemas.add(new AppSearchSchema(schemaBundles.get(i)));
+ }
+ return schemas;
}
/** Builder for {@link GetSchemaResponse} objects. */
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
index 455cf3a..8da68c0 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -59,48 +59,16 @@
* <p>Once {@link #build} is called, the instance can no longer be used.
*/
public static final class Builder {
- private String mNamespace;
+ private final String mNamespace;
private final Set<String> mUris = new ArraySet<>();
private boolean mBuilt = false;
- /**
- * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
- *
- * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
- * exists only for dogfooder transition and must be removed.
- */
- @Deprecated
- public Builder() {
- mNamespace = GenericDocument.DEFAULT_NAMESPACE;
- }
-
/** Creates a {@link RemoveByUriRequest.Builder} instance. */
public Builder(@NonNull String namespace) {
mNamespace = Preconditions.checkNotNull(namespace);
}
/**
- * Sets the namespace to remove documents for.
- *
- * <p>If this is not set, it defaults to an empty string.
- *
- * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
- * removed.
- *
- * @throws IllegalStateException if the builder has already been used.
- * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
- * exists only for dogfooder transition and must
- */
- @Deprecated
- @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.
*
* @throws IllegalStateException if the builder has already been used.
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
index 2cd08c6..646e73c 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
@@ -62,49 +62,17 @@
/** Builder for {@link ReportUsageRequest} objects. */
public static final class Builder {
- private String mNamespace;
+ private final String mNamespace;
private String mUri;
private Long mUsageTimeMillis;
private boolean mBuilt = false;
- /**
- * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
- *
- * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
- * exists only for dogfooder transition and must be removed.
- */
- @Deprecated
- public Builder() {
- mNamespace = GenericDocument.DEFAULT_NAMESPACE;
- }
-
/** Creates a {@link ReportUsageRequest.Builder} instance. */
public Builder(@NonNull String namespace) {
mNamespace = Preconditions.checkNotNull(namespace);
}
/**
- * Sets which namespace the document being used belongs to.
- *
- * <p>If this is not set, it defaults to an empty string.
- *
- * <p>TODO(b/181887768): This method exists only for dogfooder transition and must be
- * removed.
- *
- * @throws IllegalStateException if the builder has already been used
- * @deprecated Please supply the namespace in {@link #Builder(String)} instead. This method
- * exists only for dogfooder transition and must
- */
- @Deprecated
- @NonNull
- public ReportUsageRequest.Builder setNamespace(@NonNull String namespace) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(namespace);
- mNamespace = namespace;
- return this;
- }
-
- /**
* Sets the URI of the document being used.
*
* <p>This field is required.
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
index e66056f..55a228d 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -68,13 +68,6 @@
return mBundle;
}
- /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
- @NonNull
- @Deprecated
- public GenericDocument getDocument() {
- return getGenericDocument();
- }
-
/**
* Contains the matching {@link GenericDocument}.
*
@@ -142,17 +135,18 @@
* <ul>
* <li>{@link SearchSpec#RANKING_STRATEGY_NONE} - this value will be 0
* <li>{@link SearchSpec#RANKING_STRATEGY_DOCUMENT_SCORE} - the value returned by calling
- * {@link GenericDocument#getScore()} on the document returned by {@link #getDocument()}
+ * {@link GenericDocument#getScore()} on the document returned by {@link
+ * #getGenericDocument()}
* <li>{@link SearchSpec#RANKING_STRATEGY_CREATION_TIMESTAMP} - the value returned by calling
* {@link GenericDocument#getCreationTimestampMillis()} on the document returned by {@link
- * #getDocument()}
+ * #getGenericDocument()}
* <li>{@link SearchSpec#RANKING_STRATEGY_RELEVANCE_SCORE} - an arbitrary double value where a
* higher value means more relevant
* <li>{@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} - the number of times usage has been
- * reported for the document returned by {@link #getDocument()}
+ * reported for the document returned by {@link #getGenericDocument()}
* <li>{@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} - the timestamp of the
* most recent usage that has been reported for the document returned by {@link
- * #getDocument()}
+ * #getGenericDocument()}
* </ul>
*
* @return Ranking signal of the document
@@ -354,13 +348,6 @@
return mFullText;
}
- /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
- @NonNull
- @Deprecated
- public MatchRange getExactMatchPosition() {
- return getExactMatchRange();
- }
-
/**
* Gets the exact {@link MatchRange} corresponding to the given entry.
*
@@ -387,13 +374,6 @@
return getSubstring(getExactMatchRange());
}
- /** @deprecated TODO(b/181887768): This method exists only for dogfooder transition. */
- @NonNull
- @Deprecated
- public MatchRange getSnippetPosition() {
- return getSnippetRange();
- }
-
/**
* Gets the snippet {@link MatchRange} corresponding to the given entry.
*
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index e840ffc..1324451 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -109,16 +109,6 @@
}
/**
- * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
- * @deprecated This method exists only for dogfooder transition and must be removed.
- */
- @Deprecated
- @NonNull
- public Set<String> getSchemasNotVisibleToSystemUi() {
- return getSchemasNotDisplayedBySystem();
- }
-
- /**
* Returns all the schema types that are opted out of being displayed and visible on any system
* UI surface.
*/
@@ -222,17 +212,6 @@
}
/**
- * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
- * @deprecated This method exists only for dogfooder transition and must be removed.
- */
- @Deprecated
- @NonNull
- public Builder setSchemaTypeVisibilityForSystemUi(
- @NonNull String schemaType, boolean displayed) {
- return setSchemaTypeDisplayedBySystem(schemaType, displayed);
- }
-
- /**
* Sets whether or not documents from the provided {@code schemaType} will be displayed and
* visible on any system UI surface.
*
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
index 32d7e043..c9473bd 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
@@ -20,11 +20,12 @@
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.Migrator;
-import android.app.appsearch.SetSchemaResponse;
import android.app.appsearch.exceptions.AppSearchException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
+import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -35,70 +36,84 @@
* @hide
*/
public final class SchemaMigrationUtil {
+ private static final String TAG = "AppSearchMigrateUtil";
+
private SchemaMigrationUtil() {}
- /** Returns all active {@link Migrator}s that need to be triggered in this migration. */
+ /**
+ * Finds out which incompatible schema type won't be migrated by comparing its current and final
+ * version number.
+ */
@NonNull
- public static Map<String, Migrator> getActiveMigrators(
- @NonNull Set<AppSearchSchema> existingSchemas,
+ public static Set<String> getUnmigratedIncompatibleTypes(
+ @NonNull Set<String> incompatibleSchemaTypes,
@NonNull Map<String, Migrator> migrators,
- int currentVersion,
- int finalVersion) {
- if (currentVersion == finalVersion) {
- return Collections.emptyMap();
- }
- Set<String> existingTypes = new ArraySet<>(existingSchemas.size());
- for (AppSearchSchema schema : existingSchemas) {
- existingTypes.add(schema.getSchemaType());
- }
-
- Map<String, Migrator> activeMigrators = new ArrayMap<>();
- for (Map.Entry<String, Migrator> entry : migrators.entrySet()) {
- // The device contains the source type, and we should trigger migration for the type.
- String schemaType = entry.getKey();
- Migrator migrator = entry.getValue();
- if (existingTypes.contains(schemaType)
- && migrator.shouldMigrate(currentVersion, finalVersion)) {
- activeMigrators.put(schemaType, migrator);
+ @NonNull Map<String, Integer> currentVersionMap,
+ @NonNull Map<String, Integer> finalVersionMap)
+ throws AppSearchException {
+ Set<String> unmigratedSchemaTypes = new ArraySet<>();
+ for (String unmigratedSchemaType : incompatibleSchemaTypes) {
+ Integer currentVersion = currentVersionMap.get(unmigratedSchemaType);
+ Integer finalVersion = finalVersionMap.get(unmigratedSchemaType);
+ if (currentVersion == null) {
+ // impossible, we have done something wrong.
+ throw new AppSearchException(
+ AppSearchResult.RESULT_UNKNOWN_ERROR,
+ "Cannot find the current version number for schema type: "
+ + unmigratedSchemaType);
+ }
+ if (finalVersion == null) {
+ // The schema doesn't exist in the SetSchemaRequest.
+ unmigratedSchemaTypes.add(unmigratedSchemaType);
+ continue;
+ }
+ // we don't have migrator or won't trigger migration for this schema type.
+ Migrator migrator = migrators.get(unmigratedSchemaType);
+ if (migrator == null
+ || !migrator.shouldMigrate(currentVersion, finalVersion)) {
+ unmigratedSchemaTypes.add(unmigratedSchemaType);
}
}
- return activeMigrators;
+ return Collections.unmodifiableSet(unmigratedSchemaTypes);
}
/**
- * Checks the setSchema() call won't delete any types or has incompatible types after all {@link
- * Migrator} has been triggered..
+ * Triggers upgrade or downgrade migration for the given schema type if its version stored in
+ * AppSearch is different with the version in the request.
+ *
+ * @return {@code True} if we trigger the migration for the given type.
*/
- public static void checkDeletedAndIncompatibleAfterMigration(
- @NonNull SetSchemaResponse setSchemaResponse, @NonNull Set<String> activeMigrators)
+ public static boolean shouldTriggerMigration(
+ @NonNull String schemaType,
+ @NonNull Migrator migrator,
+ @NonNull Map<String, Integer> currentVersionMap,
+ @NonNull Map<String, Integer> finalVersionMap)
throws AppSearchException {
- Set<String> unmigratedIncompatibleTypes =
- new ArraySet<>(setSchemaResponse.getIncompatibleTypes());
- unmigratedIncompatibleTypes.removeAll(activeMigrators);
-
- Set<String> unmigratedDeletedTypes = new ArraySet<>(setSchemaResponse.getDeletedTypes());
- unmigratedDeletedTypes.removeAll(activeMigrators);
-
- // check if there are any unmigrated incompatible types or deleted types. If there
- // are, we will getActiveMigratorsthrow an exception. That's the only case we
- // swallowed in the AppSearchImpl#setSchema().
- // Since the force override is false, the schema will not have been set if there are
- // any incompatible or deleted types.
- checkDeletedAndIncompatible(unmigratedDeletedTypes, unmigratedIncompatibleTypes);
+ Integer currentVersion = currentVersionMap.get(schemaType);
+ Integer finalVersion = finalVersionMap.get(schemaType);
+ if (currentVersion == null) {
+ Log.d(TAG, "The SchemaType: " + schemaType + " not present in AppSearch.");
+ return false;
+ }
+ if (finalVersion == null) {
+ throw new AppSearchException(
+ AppSearchResult.RESULT_INVALID_ARGUMENT,
+ "Receive a migrator for schema type : "
+ + schemaType
+ + ", but the schema doesn't exist in the request.");
+ }
+ return migrator.shouldMigrate(currentVersion, finalVersion);
}
- /** Checks the setSchema() call won't delete any types or has incompatible types. */
- public static void checkDeletedAndIncompatible(
- @NonNull Set<String> deletedTypes, @NonNull Set<String> incompatibleTypes)
- throws AppSearchException {
- if (deletedTypes.size() > 0 || incompatibleTypes.size() > 0) {
- String newMessage =
- "Schema is incompatible."
- + "\n Deleted types: "
- + deletedTypes
- + "\n Incompatible types: "
- + incompatibleTypes;
- throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage);
+ /** Builds a Map of SchemaType and its version of given set of {@link AppSearchSchema}. */
+ //TODO(b/182620003) remove this method once support migrate to another type
+ @NonNull
+ public static Map<String, Integer> buildVersionMap(
+ @NonNull Collection<AppSearchSchema> schemas, int version) {
+ Map<String, Integer> currentVersionMap = new ArrayMap<>(schemas.size());
+ for (AppSearchSchema currentSchema : schemas) {
+ currentVersionMap.put(currentSchema.getSchemaType(), version);
}
+ return currentVersionMap;
}
}
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 6e3fb82..991dda7 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -21,6 +21,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchMigrationHelper;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
@@ -31,10 +32,13 @@
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
+import android.app.appsearch.SetSchemaResponse;
+import android.app.appsearch.StorageInfo;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -49,6 +53,11 @@
import com.android.server.SystemService;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -125,7 +134,7 @@
schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
}
AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId);
- impl.setSchema(
+ SetSchemaResponse setSchemaResponse = impl.setSchema(
packageName,
databaseName,
schemas,
@@ -133,8 +142,8 @@
schemasPackageAccessible,
forceOverride,
schemaVersion);
- invokeCallbackOnResult(
- callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
+ invokeCallbackOnResult(callback,
+ AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
} catch (Throwable t) {
invokeCallbackOnError(callback, t);
} finally {
@@ -399,6 +408,98 @@
}
@Override
+ public void writeQueryResultsToFile(
+ @NonNull String packageName,
+ @NonNull String databaseName,
+ @NonNull ParcelFileDescriptor fileDescriptor,
+ @NonNull String queryExpression,
+ @NonNull Bundle searchSpecBundle,
+ @UserIdInt int userId,
+ @NonNull IAppSearchResultCallback callback) {
+ int callingUid = Binder.getCallingUid();
+ int callingUserId = handleIncomingUser(userId, callingUid);
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ // we don't need to append the file. The file is always brand new.
+ try (DataOutputStream outputStream = new DataOutputStream(
+ new FileOutputStream(fileDescriptor.getFileDescriptor()))) {
+ SearchResultPage searchResultPage = impl.query(
+ packageName,
+ databaseName,
+ queryExpression,
+ new SearchSpec(searchSpecBundle));
+ while (!searchResultPage.getResults().isEmpty()) {
+ for (int i = 0; i < searchResultPage.getResults().size(); i++) {
+ AppSearchMigrationHelper.writeBundleToOutputStream(
+ outputStream, searchResultPage.getResults().get(i)
+ .getGenericDocument().getBundle());
+ }
+ searchResultPage = impl.getNextPage(searchResultPage.getNextPageToken());
+ }
+ }
+ invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void putDocumentsFromFile(
+ @NonNull String packageName,
+ @NonNull String databaseName,
+ @NonNull ParcelFileDescriptor fileDescriptor,
+ @UserIdInt int userId,
+ @NonNull IAppSearchResultCallback callback) {
+ int callingUid = Binder.getCallingUid();
+ int callingUserId = handleIncomingUser(userId, callingUid);
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+
+ GenericDocument document;
+ ArrayList<Bundle> migrationFailureBundles = new ArrayList<>();
+ try (DataInputStream inputStream = new DataInputStream(
+ new FileInputStream(fileDescriptor.getFileDescriptor()))) {
+ while (true) {
+ try {
+ document = AppSearchMigrationHelper
+ .readDocumentFromInputStream(inputStream);
+ } catch (EOFException e) {
+ // nothing wrong, we just finish the reading.
+ break;
+ }
+ try {
+ impl.putDocument(packageName, databaseName, document, /*logger=*/ null);
+ } catch (Throwable t) {
+ migrationFailureBundles.add(
+ new SetSchemaResponse.MigrationFailure.Builder()
+ .setNamespace(document.getNamespace())
+ .setSchemaType(document.getSchemaType())
+ .setUri(document.getUri())
+ .setAppSearchResult(
+ AppSearchResult.throwableToFailedResult(t))
+ .build().getBundle());
+ }
+ }
+ }
+ impl.persistToDisk();
+ invokeCallbackOnResult(callback,
+ AppSearchResult.newSuccessfulResult(migrationFailureBundles));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
public void reportUsage(
@NonNull String packageName,
@NonNull String databaseName,
@@ -510,6 +611,34 @@
}
@Override
+ public void getStorageInfo(
+ @NonNull String packageName,
+ @NonNull String databaseName,
+ @UserIdInt int userId,
+ @NonNull IAppSearchResultCallback callback) {
+ Preconditions.checkNotNull(packageName);
+ Preconditions.checkNotNull(databaseName);
+ Preconditions.checkNotNull(callback);
+ int callingUid = Binder.getCallingUid();
+ int callingUserId = handleIncomingUser(userId, callingUid);
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ StorageInfo storageInfo = impl.getStorageInfoForDatabase(packageName, databaseName);
+ Bundle storageInfoBundle = storageInfo.getBundle();
+ invokeCallbackOnResult(
+ callback, AppSearchResult.newSuccessfulResult(storageInfoBundle));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
public void persistToDisk(@UserIdInt int userId) {
int callingUid = Binder.getCallingUidOrThrow();
int callingUserId = handleIncomingUser(userId, callingUid);
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 8c953d1..cacf880 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -25,7 +25,6 @@
import android.content.pm.PackageManager;
import android.os.Environment;
import android.os.UserHandle;
-import android.os.storage.StorageManager;
import android.util.SparseArray;
import com.android.internal.R;
@@ -130,11 +129,7 @@
private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
// See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs
- //TODO(b/177685938):Switch from getDataUserCePackageDirectory to getDataSystemCeDirectory
- File userCeDir =
- Environment.getDataUserCePackageDirectory(
- StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName());
- return new File(userCeDir, APP_SEARCH_DIR);
+ return new File(Environment.getDataSystemCeDirectory(userId), APP_SEARCH_DIR);
}
/**
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
index 7847429..1ed26d6 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
@@ -156,7 +156,7 @@
AppSearchImpl.createPrefix(PACKAGE_NAME, DATABASE_NAME);
/** Namespace of documents that contain visibility settings */
- private static final String NAMESPACE = GenericDocument.DEFAULT_NAMESPACE;
+ private static final String NAMESPACE = "";
/**
* Prefix to add to all visibility document uri's. IcingSearchEngine doesn't allow empty uri's.
@@ -337,9 +337,9 @@
Preconditions.checkNotNull(schemasPackageAccessible);
// Persist the document
- GenericDocument.Builder visibilityDocument =
- new GenericDocument.Builder(/*uri=*/ addUriPrefix(prefix), VISIBILITY_TYPE)
- .setNamespace(NAMESPACE);
+ GenericDocument.Builder<?> visibilityDocument =
+ new GenericDocument.Builder<>(
+ NAMESPACE, /*uri=*/ addUriPrefix(prefix), VISIBILITY_TYPE);
if (!schemasNotPlatformSurfaceable.isEmpty()) {
visibilityDocument.setPropertyString(
NOT_PLATFORM_SURFACEABLE_PROPERTY,
@@ -351,17 +351,16 @@
for (Map.Entry<String, List<PackageIdentifier>> entry :
schemasPackageAccessible.entrySet()) {
for (int i = 0; i < entry.getValue().size(); i++) {
- GenericDocument packageAccessibleDocument =
- new GenericDocument.Builder(/*uri=*/ "", PACKAGE_ACCESSIBLE_TYPE)
- .setNamespace(NAMESPACE)
- .setPropertyString(
- PACKAGE_NAME_PROPERTY,
- entry.getValue().get(i).getPackageName())
- .setPropertyBytes(
- SHA_256_CERT_PROPERTY,
- entry.getValue().get(i).getSha256Certificate())
- .setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, entry.getKey())
- .build();
+ GenericDocument packageAccessibleDocument = new GenericDocument.Builder<>(
+ NAMESPACE, /*uri=*/ "", PACKAGE_ACCESSIBLE_TYPE)
+ .setPropertyString(
+ PACKAGE_NAME_PROPERTY,
+ entry.getValue().get(i).getPackageName())
+ .setPropertyBytes(
+ SHA_256_CERT_PROPERTY,
+ entry.getValue().get(i).getSha256Certificate())
+ .setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, entry.getKey())
+ .build();
packageAccessibleDocuments.add(packageAccessibleDocument);
}
schemaToPackageIdentifierMap.put(entry.getKey(), new ArraySet<>(entry.getValue()));
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 38500af..22ee501 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -258,7 +258,8 @@
public @NonNull ParcelFileDescriptor openBlob(@NonNull BlobHandle blobHandle)
throws IOException {
try {
- return mService.openBlob(blobHandle, mContext.getOpPackageName());
+ return mService.openBlob(blobHandle, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
throw new RuntimeException(e);
@@ -315,7 +316,7 @@
@CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
try {
mService.acquireLease(blobHandle, descriptionResId, null, leaseExpiryTimeMillis,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
e.maybeRethrow(LimitExceededException.class);
@@ -378,7 +379,7 @@
@CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
try {
mService.acquireLease(blobHandle, INVALID_RES_ID, description, leaseExpiryTimeMillis,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getAttributionTag());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
e.maybeRethrow(LimitExceededException.class);
@@ -497,7 +498,8 @@
*/
public void releaseLease(@NonNull BlobHandle blobHandle) throws IOException {
try {
- mService.releaseLease(blobHandle, mContext.getOpPackageName());
+ mService.releaseLease(blobHandle, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
throw new RuntimeException(e);
@@ -602,7 +604,8 @@
@Nullable
public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle) throws IOException {
try {
- return mService.getLeaseInfo(blobHandle, mContext.getOpPackageName());
+ return mService.getLeaseInfo(blobHandle, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
throw new RuntimeException(e);
@@ -897,6 +900,64 @@
}
/**
+ * Allow apps with location permission to access this blob data once it is committed using
+ * a {@link BlobHandle} representing the blob.
+ *
+ * <p> This needs to be called before committing the blob using
+ * {@link #commit(Executor, Consumer)}.
+ *
+ * Note that if a caller allows access to the blob using this API in addition to other APIs
+ * like {@link #allowPackageAccess(String, byte[])}, then apps satisfying any one of these
+ * access conditions will be allowed to access the blob.
+ *
+ * @param permissionName the name of the location permission that needs to be granted
+ * for the app. This can be either one of
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ *
+ * @throws IOException when there is an I/O error while changing the access.
+ * @throws SecurityException when the caller is not the owner of the session.
+ * @throws IllegalStateException when the caller tries to change access for a blob which is
+ * already committed.
+ */
+ public void allowPackagesWithLocationPermission(@NonNull String permissionName)
+ throws IOException {
+ try {
+ mSession.allowPackagesWithLocationPermission(permissionName);
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns {@code true} if access has been allowed for apps with location permission by
+ * using {@link #allowPackagesWithLocationPermission(String)}.
+ *
+ * @param permissionName the name of the location permission that needs to be granted
+ * for the app. This can be either one of
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ *
+ * @throws IOException when there is an I/O error while getting the access type.
+ * @throws IllegalStateException when the caller tries to get access type from a session
+ * which is closed or abandoned.
+ */
+ public boolean arePackagesWithLocationPermissionAllowed(@NonNull String permissionName)
+ throws IOException {
+ try {
+ return mSession.arePackagesWithLocationPermissionAllowed(permissionName);
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Commit the file that was written so far to this session to the blob store maintained by
* the system.
*
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index 39a9fb4..db6cb5c9 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -25,12 +25,13 @@
interface IBlobStoreManager {
long createSession(in BlobHandle handle, in String packageName);
IBlobStoreSession openSession(long sessionId, in String packageName);
- ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName);
+ ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName,
+ in String attributionTag);
void abandonSession(long sessionId, in String packageName);
void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description,
- long leaseTimeoutMillis, in String packageName);
- void releaseLease(in BlobHandle handle, in String packageName);
+ long leaseTimeoutMillis, in String packageName, in String attributionTag);
+ void releaseLease(in BlobHandle handle, in String packageName, in String attributionTag);
long getRemainingLeaseQuotaBytes(String packageName);
void waitForIdle(in RemoteCallback callback);
@@ -39,5 +40,6 @@
void deleteBlob(long blobId);
List<BlobHandle> getLeasedBlobs(in String packageName);
- LeaseInfo getLeaseInfo(in BlobHandle blobHandle, in String packageName);
+ LeaseInfo getLeaseInfo(in BlobHandle blobHandle, in String packageName,
+ in String attributionTag);
}
\ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
index 4035b96..e3ccfb8 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
@@ -26,10 +26,12 @@
void allowPackageAccess(in String packageName, in byte[] certificate);
void allowSameSignatureAccess();
void allowPublicAccess();
+ void allowPackagesWithLocationPermission(in String permissionName);
boolean isPackageAccessAllowed(in String packageName, in byte[] certificate);
boolean isSameSignatureAccessAllowed();
boolean isPublicAccessAllowed();
+ boolean arePackagesWithLocationPermissionAllowed(in String permissionName);
long getSize();
void close();
diff --git a/apex/blobstore/framework/java/android/app/blob/XmlTags.java b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
index bfc5826..6e4b2f7 100644
--- a/apex/blobstore/framework/java/android/app/blob/XmlTags.java
+++ b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
@@ -38,6 +38,7 @@
public static final String ATTR_TYPE = "t";
public static final String TAG_ALLOWED_PACKAGE = "wl";
public static final String ATTR_CERTIFICATE = "ct";
+ public static final String TAG_ALLOWED_PERMISSION = "ap";
// For BlobHandle
public static final String TAG_BLOB_HANDLE = "bh";
@@ -55,4 +56,7 @@
public static final String TAG_LEASEE = "l";
public static final String ATTR_DESCRIPTION_RES_NAME = "rn";
public static final String ATTR_DESCRIPTION = "d";
+
+ // Generic
+ public static final String ATTR_VALUE = "val";
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
index 4a527ad..ca588c5 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
@@ -15,18 +15,29 @@
*/
package com.android.server.blob;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.app.blob.XmlTags.ATTR_CERTIFICATE;
import static android.app.blob.XmlTags.ATTR_PACKAGE;
import static android.app.blob.XmlTags.ATTR_TYPE;
+import static android.app.blob.XmlTags.ATTR_VALUE;
import static android.app.blob.XmlTags.TAG_ALLOWED_PACKAGE;
+import static android.app.blob.XmlTags.TAG_ALLOWED_PERMISSION;
+
+import static com.android.server.blob.BlobStoreConfig.TAG;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.permission.PermissionManager;
import android.util.ArraySet;
import android.util.Base64;
import android.util.DebugUtils;
+import android.util.Slog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
@@ -53,21 +64,27 @@
ACCESS_TYPE_PUBLIC,
ACCESS_TYPE_SAME_SIGNATURE,
ACCESS_TYPE_ALLOWLIST,
+ ACCESS_TYPE_LOCATION_PERMISSION,
})
@interface AccessType {}
public static final int ACCESS_TYPE_PRIVATE = 1 << 0;
public static final int ACCESS_TYPE_PUBLIC = 1 << 1;
public static final int ACCESS_TYPE_SAME_SIGNATURE = 1 << 2;
public static final int ACCESS_TYPE_ALLOWLIST = 1 << 3;
+ public static final int ACCESS_TYPE_LOCATION_PERMISSION = 1 << 4;
private int mAccessType = ACCESS_TYPE_PRIVATE;
private final ArraySet<PackageIdentifier> mAllowedPackages = new ArraySet<>();
+ private final ArraySet<String> mAllowedPermissions = new ArraySet<>();
void allow(BlobAccessMode other) {
if ((other.mAccessType & ACCESS_TYPE_ALLOWLIST) != 0) {
mAllowedPackages.addAll(other.mAllowedPackages);
}
+ if ((other.mAccessType & ACCESS_TYPE_LOCATION_PERMISSION) != 0) {
+ mAllowedPermissions.addAll(other.mAllowedPermissions);
+ }
mAccessType |= other.mAccessType;
}
@@ -84,6 +101,11 @@
mAllowedPackages.add(PackageIdentifier.create(packageName, certificate));
}
+ void allowPackagesWithLocationPermission(@NonNull String permissionName) {
+ mAccessType |= ACCESS_TYPE_LOCATION_PERMISSION;
+ mAllowedPermissions.add(permissionName);
+ }
+
boolean isPublicAccessAllowed() {
return (mAccessType & ACCESS_TYPE_PUBLIC) != 0;
}
@@ -99,8 +121,15 @@
return mAllowedPackages.contains(PackageIdentifier.create(packageName, certificate));
}
- boolean isAccessAllowedForCaller(Context context,
- @NonNull String callingPackage, @NonNull String committerPackage) {
+ boolean arePackagesWithLocationPermissionAllowed(@NonNull String permissionName) {
+ if ((mAccessType & ACCESS_TYPE_LOCATION_PERMISSION) == 0) {
+ return false;
+ }
+ return mAllowedPermissions.contains(permissionName);
+ }
+
+ boolean isAccessAllowedForCaller(Context context, @NonNull String callingPackage,
+ @NonNull String committerPackage, int callingUid, @Nullable String attributionTag) {
if ((mAccessType & ACCESS_TYPE_PUBLIC) != 0) {
return true;
}
@@ -124,9 +153,37 @@
}
}
+ if ((mAccessType & ACCESS_TYPE_LOCATION_PERMISSION) != 0) {
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ for (int i = 0; i < mAllowedPermissions.size(); ++i) {
+ final String permission = mAllowedPermissions.valueAt(i);
+ if (PermissionManager.checkPackageNamePermission(permission, callingPackage,
+ UserHandle.getUserId(callingUid)) != PackageManager.PERMISSION_GRANTED) {
+ continue;
+ }
+ // TODO: Add appropriate message
+ if (appOpsManager.noteOpNoThrow(getAppOp(permission), callingUid, callingPackage,
+ attributionTag, null /* message */) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
+ }
+ }
+
return false;
}
+ private static String getAppOp(String permission) {
+ switch (permission) {
+ case ACCESS_FINE_LOCATION:
+ return AppOpsManager.OPSTR_FINE_LOCATION;
+ case ACCESS_COARSE_LOCATION:
+ return AppOpsManager.OPSTR_COARSE_LOCATION;
+ default:
+ Slog.w(TAG, "Unknown permission found: " + permission);
+ return null;
+ }
+ }
+
int getAccessType() {
return mAccessType;
}
@@ -148,6 +205,16 @@
}
fout.decreaseIndent();
}
+ fout.print("Allowed permissions:");
+ if (mAllowedPermissions.isEmpty()) {
+ fout.println(" (Empty)");
+ } else {
+ fout.increaseIndent();
+ for (int i = 0, count = mAllowedPermissions.size(); i < count; ++i) {
+ fout.println(mAllowedPermissions.valueAt(i).toString());
+ }
+ fout.decreaseIndent();
+ }
}
void writeToXml(@NonNull XmlSerializer out) throws IOException {
@@ -159,6 +226,12 @@
XmlUtils.writeByteArrayAttribute(out, ATTR_CERTIFICATE, packageIdentifier.certificate);
out.endTag(null, TAG_ALLOWED_PACKAGE);
}
+ for (int i = 0, count = mAllowedPermissions.size(); i < count; ++i) {
+ out.startTag(null, TAG_ALLOWED_PERMISSION);
+ final String permission = mAllowedPermissions.valueAt(i);
+ XmlUtils.writeStringAttribute(out, ATTR_VALUE, permission);
+ out.endTag(null, TAG_ALLOWED_PERMISSION);
+ }
}
@NonNull
@@ -176,6 +249,10 @@
final byte[] certificate = XmlUtils.readByteArrayAttribute(in, ATTR_CERTIFICATE);
blobAccessMode.allowPackageAccess(packageName, certificate);
}
+ if (TAG_ALLOWED_PERMISSION.equals(in.getName())) {
+ final String permission = XmlUtils.readStringAttribute(in, ATTR_VALUE);
+ blobAccessMode.allowPackagesWithLocationPermission(permission);
+ }
}
return blobAccessMode;
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index fb02e96..8b12beb 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -229,7 +229,8 @@
return getBlobFile().length();
}
- boolean isAccessAllowedForCaller(@NonNull String callingPackage, int callingUid) {
+ boolean isAccessAllowedForCaller(@NonNull String callingPackage, int callingUid,
+ @Nullable String attributionTag) {
// Don't allow the blob to be accessed after it's expiry time has passed.
if (getBlobHandle().isExpired()) {
return false;
@@ -254,7 +255,7 @@
// Check if the caller is allowed access as per the access mode specified
// by the committer.
if (committer.blobAccessMode.isAccessAllowedForCaller(mContext,
- callingPackage, committer.packageName)) {
+ callingPackage, committer.packageName, callingUid, attributionTag)) {
return true;
}
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index f77f6c6..0e73547 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -402,12 +402,12 @@
}
private ParcelFileDescriptor openBlobInternal(BlobHandle blobHandle, int callingUid,
- String callingPackage) throws IOException {
+ String callingPackage, String attributionTag) throws IOException {
synchronized (mBlobsLock) {
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
- callingPackage, callingUid)) {
+ callingPackage, callingUid, attributionTag)) {
if (blobMetadata == null) {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
INVALID_BLOB_ID, INVALID_BLOB_SIZE,
@@ -455,7 +455,7 @@
private void acquireLeaseInternal(BlobHandle blobHandle, int descriptionResId,
CharSequence description, long leaseExpiryTimeMillis,
- int callingUid, String callingPackage) {
+ int callingUid, String callingPackage, String attributionTag) {
synchronized (mBlobsLock) {
final int leasesCount = getLeasedBlobsCountLocked(callingUid, callingPackage);
if (leasesCount >= getMaxLeasedBlobs()) {
@@ -468,7 +468,7 @@
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
- callingPackage, callingUid)) {
+ callingPackage, callingUid, attributionTag)) {
if (blobMetadata == null) {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
INVALID_BLOB_ID, INVALID_BLOB_SIZE,
@@ -527,13 +527,13 @@
}
private void releaseLeaseInternal(BlobHandle blobHandle, int callingUid,
- String callingPackage) {
+ String callingPackage, String attributionTag) {
synchronized (mBlobsLock) {
final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
getUserBlobsLocked(UserHandle.getUserId(callingUid));
final BlobMetadata blobMetadata = userBlobs.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
- callingPackage, callingUid)) {
+ callingPackage, callingUid, attributionTag)) {
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
@@ -634,12 +634,12 @@
}
private LeaseInfo getLeaseInfoInternal(BlobHandle blobHandle,
- int callingUid, @NonNull String callingPackage) {
+ int callingUid, @NonNull String callingPackage, String attributionTag) {
synchronized (mBlobsLock) {
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
- callingPackage, callingUid)) {
+ callingPackage, callingUid, attributionTag)) {
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
@@ -1465,7 +1465,7 @@
@Override
public ParcelFileDescriptor openBlob(@NonNull BlobHandle blobHandle,
- @NonNull String packageName) {
+ @NonNull String packageName, @Nullable String attributionTag) {
Objects.requireNonNull(blobHandle, "blobHandle must not be null");
blobHandle.assertIsValid();
Objects.requireNonNull(packageName, "packageName must not be null");
@@ -1480,7 +1480,7 @@
}
try {
- return openBlobInternal(blobHandle, callingUid, packageName);
+ return openBlobInternal(blobHandle, callingUid, packageName, attributionTag);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
@@ -1489,7 +1489,8 @@
@Override
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
@Nullable CharSequence description,
- @CurrentTimeSecondsLong long leaseExpiryTimeMillis, @NonNull String packageName) {
+ @CurrentTimeSecondsLong long leaseExpiryTimeMillis, @NonNull String packageName,
+ @Nullable String attributionTag) {
Objects.requireNonNull(blobHandle, "blobHandle must not be null");
blobHandle.assertIsValid();
Preconditions.checkArgument(
@@ -1513,7 +1514,7 @@
try {
acquireLeaseInternal(blobHandle, descriptionResId, description,
- leaseExpiryTimeMillis, callingUid, packageName);
+ leaseExpiryTimeMillis, callingUid, packageName, attributionTag);
} catch (Resources.NotFoundException e) {
throw new IllegalArgumentException(e);
} catch (LimitExceededException e) {
@@ -1522,7 +1523,8 @@
}
@Override
- public void releaseLease(@NonNull BlobHandle blobHandle, @NonNull String packageName) {
+ public void releaseLease(@NonNull BlobHandle blobHandle, @NonNull String packageName,
+ @Nullable String attributionTag) {
Objects.requireNonNull(blobHandle, "blobHandle must not be null");
blobHandle.assertIsValid();
Objects.requireNonNull(packageName, "packageName must not be null");
@@ -1536,7 +1538,7 @@
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
- releaseLeaseInternal(blobHandle, callingUid, packageName);
+ releaseLeaseInternal(blobHandle, callingUid, packageName, attributionTag);
}
@Override
@@ -1606,7 +1608,8 @@
@Override
@Nullable
- public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle, @NonNull String packageName) {
+ public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle, @NonNull String packageName,
+ @Nullable String attributionTag) {
Objects.requireNonNull(blobHandle, "blobHandle must not be null");
blobHandle.assertIsValid();
Objects.requireNonNull(packageName, "packageName must not be null");
@@ -1620,7 +1623,7 @@
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
- return getLeaseInfoInternal(blobHandle, callingUid, packageName);
+ return getLeaseInfoInternal(blobHandle, callingUid, packageName, attributionTag);
}
@Override
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index fe68882..2c3f682 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -15,6 +15,8 @@
*/
package com.android.server.blob;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.app.blob.BlobStoreManager.COMMIT_RESULT_ERROR;
import static android.app.blob.XmlTags.ATTR_CREATION_TIME_MS;
import static android.app.blob.XmlTags.ATTR_ID;
@@ -366,6 +368,21 @@
}
@Override
+ public void allowPackagesWithLocationPermission(@NonNull String permissionName) {
+ assertCallerIsOwner();
+ Preconditions.checkArgument(ACCESS_FINE_LOCATION.equals(permissionName)
+ || ACCESS_COARSE_LOCATION.equals(permissionName),
+ "permissionName is unknown: " + permissionName);
+ synchronized (mSessionLock) {
+ if (mState != STATE_OPENED) {
+ throw new IllegalStateException("Not allowed to change access type in state: "
+ + stateToString(mState));
+ }
+ mBlobAccessMode.allowPackagesWithLocationPermission(permissionName);
+ }
+ }
+
+ @Override
public boolean isPackageAccessAllowed(@NonNull String packageName,
@NonNull byte[] certificate) {
assertCallerIsOwner();
@@ -406,6 +423,21 @@
}
@Override
+ public boolean arePackagesWithLocationPermissionAllowed(@NonNull String permissionName) {
+ assertCallerIsOwner();
+ Preconditions.checkArgument(ACCESS_FINE_LOCATION.equals(permissionName)
+ || ACCESS_COARSE_LOCATION.equals(permissionName),
+ "permissionName is unknown: " + permissionName);
+ synchronized (mSessionLock) {
+ if (mState != STATE_OPENED) {
+ throw new IllegalStateException("Not allowed to change access type in state: "
+ + stateToString(mState));
+ }
+ return mBlobAccessMode.arePackagesWithLocationPermissionAllowed(permissionName);
+ }
+ }
+
+ @Override
public void close() {
closeSession(STATE_CLOSED, false /* sendCallback */);
}
diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp
index b51c2aa..f766fcf 100644
--- a/apex/jobscheduler/framework/Android.bp
+++ b/apex/jobscheduler/framework/Android.bp
@@ -32,6 +32,7 @@
},
libs: [
"app-compat-annotations",
+ "framework-connectivity.stubs.module_lib",
"framework-minus-apex",
"unsupportedappusage",
],
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 78c5b15..3ea1922 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
@@ -29,6 +30,7 @@
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -42,7 +44,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
+import java.util.Objects;
import java.util.WeakHashMap;
+import java.util.concurrent.Executor;
/**
* This class provides access to the system alarm services. These allow you
@@ -194,6 +198,15 @@
public static final int FLAG_ALLOW_WHILE_IDLE_COMPAT = 1 << 5;
/**
+ * Flag for alarms: Used to mark prioritized alarms. These alarms will get to execute while idle
+ * and can be sent separately from other alarms that may be already due at the time.
+ * These alarms can be set via
+ * {@link #setPrioritized(int, long, long, String, Executor, OnAlarmListener)}
+ * @hide
+ */
+ public static final int FLAG_PRIORITIZE = 1 << 6;
+
+ /**
* For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
* {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
* {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new
@@ -227,15 +240,15 @@
final class ListenerWrapper extends IAlarmListener.Stub implements Runnable {
final OnAlarmListener mListener;
- Handler mHandler;
+ Executor mExecutor;
IAlarmCompleteListener mCompletion;
public ListenerWrapper(OnAlarmListener listener) {
mListener = listener;
}
- public void setHandler(Handler h) {
- mHandler = h;
+ void setExecutor(Executor e) {
+ mExecutor = e;
}
public void cancel() {
@@ -250,7 +263,7 @@
public void doAlarm(IAlarmCompleteListener alarmManager) {
mCompletion = alarmManager;
- mHandler.post(this);
+ mExecutor.execute(this);
}
@Override
@@ -368,7 +381,7 @@
*/
public void set(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null,
- null, null, null);
+ (Handler) null, null, null);
}
/**
@@ -457,7 +470,7 @@
public void setRepeating(@AlarmType int type, long triggerAtMillis,
long intervalMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation,
- null, null, null, null, null);
+ null, null, (Handler) null, null, null);
}
/**
@@ -507,7 +520,7 @@
public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
PendingIntent operation) {
setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, operation,
- null, null, null, null, null);
+ null, null, (Handler) null, null, null);
}
/**
@@ -526,6 +539,53 @@
}
/**
+ * Schedule an alarm that is prioritized by the system while the device is in power saving modes
+ * such as battery saver and device idle (doze).
+ *
+ * <p>
+ * Apps that use this are not guaranteed to get all alarms as requested during power saving
+ * modes, i.e. the system may still impose restrictions on how frequently these alarms will go
+ * off for a particular application, like requiring a certain minimum duration be elapsed
+ * between consecutive alarms. This duration will be normally be in the order of a few minutes.
+ *
+ * <p>
+ * When the system wakes up to deliver these alarms, it may not deliver any of the other pending
+ * alarms set earlier by the calling app, even the special ones set via
+ * {@link #setAndAllowWhileIdle(int, long, PendingIntent)} or
+ * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)}. So the caller should not
+ * expect these to arrive in any relative order to its other alarms.
+ *
+ * @param type type of alarm
+ * @param windowStartMillis The earliest time, in milliseconds, that the alarm should
+ * be delivered, expressed in the appropriate clock's units (depending on the alarm
+ * type).
+ * @param windowLengthMillis The length of the requested delivery window,
+ * in milliseconds. The alarm will be delivered no later than this many
+ * milliseconds after {@code windowStartMillis}. Note that this parameter
+ * is a <i>duration,</i> not the timestamp of the end of the window.
+ * @param tag string describing the alarm, used for logging and battery-use
+ * attribution
+ * @param listener {@link OnAlarmListener} instance whose
+ * {@link OnAlarmListener#onAlarm() onAlarm()} method will be
+ * called when the alarm time is reached. A given OnAlarmListener instance can
+ * only be the target of a single pending alarm, just as a given PendingIntent
+ * can only be used with one alarm at a time.
+ * @param executor {@link Executor} on which to execute the listener's onAlarm()
+ * callback.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.SCHEDULE_PRIORITIZED_ALARM)
+ public void setPrioritized(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
+ @NonNull String tag, @NonNull Executor executor, @NonNull OnAlarmListener listener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(tag);
+ Objects.requireNonNull(listener);
+ setImpl(type, windowStartMillis, windowLengthMillis, 0, FLAG_PRIORITIZE, null, listener,
+ tag, executor, null, null);
+ }
+
+ /**
* Schedule an alarm to be delivered precisely at the stated time.
*
* <p>
@@ -565,7 +625,7 @@
*/
@RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
public void setExact(@AlarmType int type, long triggerAtMillis, PendingIntent operation) {
- setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, null,
+ setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null, (Handler) null,
null, null);
}
@@ -645,7 +705,7 @@
@RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
- null, null, null, null, info);
+ null, null, (Handler) null, null, info);
}
/** @hide */
@@ -654,7 +714,7 @@
public void set(@AlarmType int type, long triggerAtMillis, long windowMillis,
long intervalMillis, PendingIntent operation, WorkSource workSource) {
setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, null, null,
- null, workSource, null);
+ (Handler) null, workSource, null);
}
/**
@@ -698,6 +758,15 @@
long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,
String listenerTag, Handler targetHandler, WorkSource workSource,
AlarmClockInfo alarmClock) {
+ final Handler handlerToUse = (targetHandler != null) ? targetHandler : mMainThreadHandler;
+ setImpl(type, triggerAtMillis, windowMillis, intervalMillis, flags, operation, listener,
+ listenerTag, new HandlerExecutor(handlerToUse), workSource, alarmClock);
+ }
+
+ private void setImpl(@AlarmType int type, long triggerAtMillis, long windowMillis,
+ long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,
+ String listenerTag, Executor targetExecutor, WorkSource workSource,
+ AlarmClockInfo alarmClock) {
if (triggerAtMillis < 0) {
/* NOTYET
if (mAlwaysExact) {
@@ -726,9 +795,7 @@
sWrappers.put(listener, new WeakReference<>(recipientWrapper));
}
}
-
- final Handler handler = (targetHandler != null) ? targetHandler : mMainThreadHandler;
- recipientWrapper.setHandler(handler);
+ recipientWrapper.setExecutor(targetExecutor);
}
try {
@@ -834,7 +901,7 @@
public void setInexactRepeating(@AlarmType int type, long triggerAtMillis,
long intervalMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null,
- null, null, null, null);
+ null, (Handler) null, null, null);
}
/**
@@ -884,7 +951,7 @@
public void setAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE,
- operation, null, null, null, null, null);
+ operation, null, null, (Handler) null, null, null);
}
/**
@@ -945,7 +1012,7 @@
public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
- null, null, null, null, null);
+ null, null, (Handler) null, null, null);
}
/**
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 63b3959..cb1fccf 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -83,6 +83,12 @@
}
/**
+ * Does not place the app on any temporary allow list. Nullifies the previous call to
+ * {@link android.app.BroadcastOptions#setTemporaryAppAllowlist(long, int, int, String)}.
+ * Note: this will not remove the receiver app from the temp allow list.
+ */
+ public static final int TEMPORARY_ALLOW_LIST_TYPE_NONE = -1;
+ /**
* Allow the temp allow list behavior, plus allow foreground service start from background.
*/
public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
@@ -96,6 +102,7 @@
* @hide
*/
@IntDef(flag = true, prefix = { "TEMPORARY_ALLOW_LIST_TYPE_" }, value = {
+ TEMPORARY_ALLOW_LIST_TYPE_NONE,
TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
})
@@ -378,6 +385,8 @@
/**
* Add the specified package to the permanent power save allow list.
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public void addToPermanentAllowList(@NonNull String packageName) {
@@ -386,6 +395,8 @@
/**
* Add the specified packages to the permanent power save allow list.
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public void addToPermanentAllowList(@NonNull List<String> packageNames) {
@@ -402,6 +413,7 @@
*
* @param includingIdle Set to true if the app should be allow-listed from device idle as well
* as other power save restrictions
+ * @hide
*/
@NonNull
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
@@ -443,6 +455,7 @@
* removed. Apps allow-listed by default by the system cannot be removed.
*
* @param packageName The app to remove from the allow list
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public void removeFromPermanentAllowList(@NonNull String packageName) {
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 9bd57a1..a9ca5cf 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -17,9 +17,9 @@
package com.android.server;
import android.annotation.Nullable;
-import android.os.PowerWhitelistManager;
-import android.os.PowerWhitelistManager.ReasonCode;
-import android.os.PowerWhitelistManager.TempAllowListType;
+import android.os.PowerExemptionManager;
+import android.os.PowerExemptionManager.ReasonCode;
+import android.os.PowerExemptionManager.TempAllowListType;
import com.android.server.deviceidle.IDeviceIdleConstraint;
@@ -35,7 +35,7 @@
/**
* Same as {@link #addPowerSaveTempWhitelistApp(int, String, long, int, boolean, int, String)}
- * with {@link PowerWhitelistManager#TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED}.
+ * with {@link PowerExemptionManager#TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED}.
*/
void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
long durationMs, int userId, boolean sync, @ReasonCode int reasonCode,
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 667fc60..57c8300 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -16,9 +16,9 @@
package com.android.server;
-import static android.os.PowerWhitelistManager.REASON_SHELL;
-import static android.os.PowerWhitelistManager.REASON_UNKNOWN;
-import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.PowerExemptionManager.REASON_SHELL;
+import static android.os.PowerExemptionManager.REASON_UNKNOWN;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Process.INVALID_UID;
import android.Manifest;
@@ -58,11 +58,11 @@
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerExemptionManager.ReasonCode;
+import android.os.PowerExemptionManager.TempAllowListType;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
-import android.os.PowerWhitelistManager.ReasonCode;
-import android.os.PowerWhitelistManager.TempAllowListType;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -1947,7 +1947,7 @@
long durationMs, int userId, boolean sync, @ReasonCode int reasonCode,
@Nullable String reason) {
addPowerSaveTempAllowlistAppInternal(callingUid, packageName, durationMs,
- TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
userId, sync, reasonCode, reason);
}
@@ -2706,7 +2706,7 @@
final long token = Binder.clearCallingIdentity();
try {
addPowerSaveTempAllowlistAppInternal(callingUid,
- packageName, duration, TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ packageName, duration, TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
userId, true, reasonCode, reason);
} finally {
Binder.restoreCallingIdentity(token);
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 58fc874..3c9496f 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -23,6 +23,7 @@
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
+import static android.app.AlarmManager.FLAG_PRIORITIZE;
import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
import static android.app.AlarmManager.INTERVAL_DAY;
import static android.app.AlarmManager.INTERVAL_HOUR;
@@ -100,6 +101,7 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -221,6 +223,7 @@
AlarmHandler mHandler;
AppWakeupHistory mAppWakeupHistory;
AppWakeupHistory mAllowWhileIdleHistory;
+ private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
ClockReceiver mClockReceiver;
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
IBinder.DeathRecipient mListenerDeathRecipient;
@@ -432,6 +435,8 @@
@VisibleForTesting
static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps";
+ @VisibleForTesting
+ static final String KEY_PRIORITY_ALARM_DELAY = "priority_alarm_delay";
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
@@ -470,6 +475,8 @@
// TODO (b/171306433): Change to true by default.
private static final boolean DEFAULT_CRASH_NON_CLOCK_APPS = false;
+ private static final long DEFAULT_PRIORITY_ALARM_DELAY = 9 * 60_000;
+
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -525,6 +532,12 @@
*/
public boolean CRASH_NON_CLOCK_APPS = DEFAULT_CRASH_NON_CLOCK_APPS;
+ /**
+ * Minimum delay between two slots that an app can get for their prioritized alarms, while
+ * the device is in doze.
+ */
+ public long PRIORITY_ALARM_DELAY = DEFAULT_PRIORITY_ALARM_DELAY;
+
private long mLastAllowWhileIdleWhitelistDuration = -1;
Constants() {
@@ -662,6 +675,10 @@
CRASH_NON_CLOCK_APPS = properties.getBoolean(KEY_CRASH_NON_CLOCK_APPS,
DEFAULT_CRASH_NON_CLOCK_APPS);
break;
+ case KEY_PRIORITY_ALARM_DELAY:
+ PRIORITY_ALARM_DELAY = properties.getLong(KEY_PRIORITY_ALARM_DELAY,
+ DEFAULT_PRIORITY_ALARM_DELAY);
+ break;
default:
if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
// The quotas need to be updated in order, so we can't just rely
@@ -809,6 +826,11 @@
pw.print(KEY_CRASH_NON_CLOCK_APPS, CRASH_NON_CLOCK_APPS);
pw.println();
+ pw.print(KEY_PRIORITY_ALARM_DELAY);
+ pw.print("=");
+ TimeUtils.formatDuration(PRIORITY_ALARM_DELAY, pw);
+ pw.println();
+
pw.decreaseIndent();
}
@@ -1794,6 +1816,11 @@
batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
alarm.sourcePackage, userId, quota) + window;
}
+ } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
+ final long lastDispatch = mLastPriorityAlarmDispatch.get(alarm.creatorUid, 0);
+ batterySaverPolicyElapsed = (lastDispatch == 0)
+ ? nowElapsed
+ : lastDispatch + mConstants.PRIORITY_ALARM_DELAY;
} else {
// Not allowed.
batterySaverPolicyElapsed = nowElapsed + INDEFINITE_DELAY;
@@ -1849,6 +1876,12 @@
alarm.sourcePackage, userId, quota) + window;
deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
}
+ } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
+ final long lastDispatch = mLastPriorityAlarmDispatch.get(alarm.creatorUid, 0);
+ final long whenAllowed = (lastDispatch == 0)
+ ? nowElapsed
+ : lastDispatch + mConstants.PRIORITY_ALARM_DELAY;
+ deviceIdlePolicyTime = Math.min(whenAllowed, mPendingIdleUntil.getWhenElapsed());
} else {
// Not allowed.
deviceIdlePolicyTime = mPendingIdleUntil.getWhenElapsed();
@@ -2025,7 +2058,12 @@
// make sure the caller is allowed to use the requested kind of alarm, and also
// decide what quota and broadcast options to use.
Bundle idleOptions = null;
- if (exact || allowWhileIdle) {
+ if ((flags & FLAG_PRIORITIZE) != 0) {
+ getContext().enforcePermission(
+ Manifest.permission.SCHEDULE_PRIORITIZED_ALARM,
+ Binder.getCallingPid(), callingUid, "AlarmManager.setPrioritized");
+ flags &= ~(FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_COMPAT);
+ } else if (exact || allowWhileIdle) {
final boolean needsPermission;
boolean lowerQuota;
if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
@@ -2107,6 +2145,7 @@
flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
flags &= ~FLAG_ALLOW_WHILE_IDLE;
flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT;
+ flags &= ~FLAG_PRIORITIZE;
idleOptions = null;
}
@@ -2489,6 +2528,19 @@
pw.println("Allow while idle history:");
mAllowWhileIdleHistory.dump(pw, nowELAPSED);
+ if (mLastPriorityAlarmDispatch.size() > 0) {
+ pw.println("Last priority alarm dispatches:");
+ pw.increaseIndent();
+ for (int i = 0; i < mLastPriorityAlarmDispatch.size(); i++) {
+ pw.print("UID: ");
+ UserHandle.formatUid(pw, mLastPriorityAlarmDispatch.keyAt(i));
+ pw.print(": ");
+ TimeUtils.formatDuration(mLastPriorityAlarmDispatch.valueAt(i), nowELAPSED, pw);
+ pw.println();
+ }
+ pw.decreaseIndent();
+ }
+
if (mLog.dump(pw, "Recent problems:")) {
pw.println();
}
@@ -3303,6 +3355,11 @@
mPendingBackgroundAlarms.removeAt(i);
}
}
+ for (int i = mLastPriorityAlarmDispatch.size() - 1; i >= 0; i--) {
+ if (UserHandle.getUserId(mLastPriorityAlarmDispatch.keyAt(i)) == userHandle) {
+ mLastPriorityAlarmDispatch.removeAt(i);
+ }
+ }
if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) {
mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
if (mPendingIdleUntil != null) {
@@ -4103,6 +4160,7 @@
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
sdFilter.addAction(Intent.ACTION_USER_STOPPED);
+ sdFilter.addAction(Intent.ACTION_UID_REMOVED);
getContext().registerReceiver(this, sdFilter);
}
@@ -4132,6 +4190,9 @@
mAllowWhileIdleHistory.removeForUser(userHandle);
}
return;
+ case Intent.ACTION_UID_REMOVED:
+ mLastPriorityAlarmDispatch.delete(uid);
+ return;
case Intent.ACTION_PACKAGE_REMOVED:
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// This package is being updated; don't kill its alarms.
@@ -4522,11 +4583,11 @@
if (inflight.isBroadcast()) {
notifyBroadcastAlarmPendingLocked(alarm.uid);
}
- if (isAllowedWhileIdleRestricted(alarm)) {
- final boolean doze = (mPendingIdleUntil != null);
- final boolean batterySaver = (mAppStateTracker != null
- && mAppStateTracker.isForceAllAppsStandbyEnabled());
- if (doze || batterySaver) {
+ final boolean doze = (mPendingIdleUntil != null);
+ final boolean batterySaver = (mAppStateTracker != null
+ && mAppStateTracker.isForceAllAppsStandbyEnabled());
+ if (doze || batterySaver) {
+ if (isAllowedWhileIdleRestricted(alarm)) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm while the
// device was in doze or battery saver.
mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
@@ -4538,6 +4599,16 @@
return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
|| (batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a));
});
+ } else if ((alarm.flags & FLAG_PRIORITIZE) != 0) {
+ mLastPriorityAlarmDispatch.put(alarm.creatorUid, nowELAPSED);
+ mAlarmStore.updateAlarmDeliveries(a -> {
+ if (a.creatorUid != alarm.creatorUid
+ || (alarm.flags & FLAG_PRIORITIZE) == 0) {
+ return false;
+ }
+ return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
+ || (batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a));
+ });
}
if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 8ac237e..a452fbc 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1046,7 +1046,7 @@
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
uId, null, jobStatus.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
- JobProtoEnums.STOP_REASON_CANCELLED, jobStatus.getStandbyBucket(),
+ JobProtoEnums.STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(),
jobStatus.getJobId(),
jobStatus.hasChargingConstraint(),
jobStatus.hasBatteryNotLowConstraint(),
@@ -1055,7 +1055,9 @@
jobStatus.hasDeadlineConstraint(),
jobStatus.hasIdleConstraint(),
jobStatus.hasConnectivityConstraint(),
- jobStatus.hasContentTriggerConstraint());
+ jobStatus.hasContentTriggerConstraint(),
+ jobStatus.isRequestedExpeditedJob(),
+ /* isRunningAsExpeditedJob */ false);
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 790fae0..44b3e3e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -280,7 +280,7 @@
if (job.shouldTreatAsExpeditedJob()) {
bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_ALMOST_PERCEPTIBLE
- | Context.BIND_ALLOW_NETWORK_ACCESS
+ | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
| Context.BIND_NOT_APP_COMPONENT_USAGE;
} else {
bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
@@ -322,7 +322,9 @@
job.hasDeadlineConstraint(),
job.hasIdleConstraint(),
job.hasConnectivityConstraint(),
- job.hasContentTriggerConstraint());
+ job.hasContentTriggerConstraint(),
+ job.isRequestedExpeditedJob(),
+ job.shouldTreatAsExpeditedJob());
try {
mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
} catch (RemoteException e) {
@@ -904,7 +906,9 @@
completedJob.hasDeadlineConstraint(),
completedJob.hasIdleConstraint(),
completedJob.hasConnectivityConstraint(),
- completedJob.hasContentTriggerConstraint());
+ completedJob.hasContentTriggerConstraint(),
+ completedJob.isRequestedExpeditedJob(),
+ completedJob.startedAsExpeditedJob);
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
legacyStopReason);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index df21d75..1e78ec3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -27,11 +27,8 @@
import android.app.job.JobInfo;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
-import android.net.INetworkPolicyListener;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkPolicyManager;
import android.net.NetworkRequest;
import android.os.Handler;
import android.os.Looper;
@@ -43,8 +40,10 @@
import android.util.DataUnit;
import android.util.IndentingPrintWriter;
import android.util.Log;
+import android.util.Pools;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -55,6 +54,9 @@
import com.android.server.job.StateControllerProto;
import com.android.server.net.NetworkPolicyManagerInternal;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
@@ -73,8 +75,15 @@
private static final boolean DEBUG = JobSchedulerService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
+ // The networking stack has a hard limit so we can't make this configurable.
+ private static final int MAX_NETWORK_CALLBACKS = 50;
+ /**
+ * Minimum amount of time that should have elapsed before we'll update a {@link UidStats}
+ * instance.
+ */
+ private static final long MIN_STATS_UPDATE_INTERVAL_MS = 30_000L;
+
private final ConnectivityManager mConnManager;
- private final NetworkPolicyManager mNetPolicyManager;
private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;
/** List of tracked jobs keyed by source UID. */
@@ -95,8 +104,69 @@
@GuardedBy("mLock")
private final ArrayMap<Network, NetworkCapabilities> mAvailableNetworks = new ArrayMap<>();
- private static final int MSG_DATA_SAVER_TOGGLED = 0;
- private static final int MSG_UID_RULES_CHANGES = 1;
+ private final SparseArray<UidDefaultNetworkCallback> mCurrentDefaultNetworkCallbacks =
+ new SparseArray<>();
+ private final Comparator<UidStats> mUidStatsComparator = new Comparator<UidStats>() {
+ private int prioritizeExistence(int v1, int v2) {
+ if (v1 > 0 && v2 > 0) {
+ return 0;
+ }
+ return v2 - v1;
+ }
+
+ @Override
+ public int compare(UidStats us1, UidStats us2) {
+ // TODO: build a better prioritization scheme
+ // Some things to use:
+ // * Proc state
+ // * IMPORTANT_WHILE_IN_FOREGROUND bit
+ final int runningPriority = prioritizeExistence(us1.numRunning, us2.numRunning);
+ if (runningPriority != 0) {
+ return runningPriority;
+ }
+ // Prioritize any UIDs that have jobs that would be ready ahead of UIDs that don't.
+ final int readyWithConnPriority =
+ prioritizeExistence(us1.numReadyWithConnectivity, us2.numReadyWithConnectivity);
+ if (readyWithConnPriority != 0) {
+ return readyWithConnPriority;
+ }
+ // They both have jobs that would be ready. Prioritize the UIDs whose requested
+ // network is available ahead of UIDs that don't have their requested network available.
+ final int reqAvailPriority = prioritizeExistence(
+ us1.numRequestedNetworkAvailable, us2.numRequestedNetworkAvailable);
+ if (reqAvailPriority != 0) {
+ return reqAvailPriority;
+ }
+ // They both have jobs with available networks. Prioritize based on:
+ // 1. (eventually) proc state
+ // 2. Existence of runnable EJs (not just requested)
+ // 3. Enqueue time
+ // TODO: maybe consider number of jobs
+ final int ejPriority = prioritizeExistence(us1.numEJs, us2.numEJs);
+ if (ejPriority != 0) {
+ return ejPriority;
+ }
+ // They both have EJs. Order them by EJ enqueue time to help provide low EJ latency.
+ if (us1.earliestEJEnqueueTime < us2.earliestEJEnqueueTime) {
+ return -1;
+ } else if (us1.earliestEJEnqueueTime > us2.earliestEJEnqueueTime) {
+ return 1;
+ }
+ if (us1.earliestEnqueueTime < us2.earliestEnqueueTime) {
+ return -1;
+ }
+ return us1.earliestEnqueueTime > us2.earliestEnqueueTime ? 1 : 0;
+ }
+ };
+ private final SparseArray<UidStats> mUidStats = new SparseArray<>();
+ private final Pools.Pool<UidDefaultNetworkCallback> mDefaultNetworkCallbackPool =
+ new Pools.SimplePool<>(MAX_NETWORK_CALLBACKS);
+ /**
+ * List of UidStats, sorted by priority as defined in {@link #mUidStatsComparator}. The sorting
+ * is only done in {@link #maybeAdjustRegisteredCallbacksLocked()} and may sometimes be stale.
+ */
+ private final List<UidStats> mSortedStats = new ArrayList<>();
+
private static final int MSG_REEVALUATE_JOBS = 2;
private final Handler mHandler;
@@ -106,22 +176,26 @@
mHandler = new CcHandler(mContext.getMainLooper());
mConnManager = mContext.getSystemService(ConnectivityManager.class);
- mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
mNetPolicyManagerInternal = LocalServices.getService(NetworkPolicyManagerInternal.class);
// We're interested in all network changes; internally we match these
// network changes against the active network for each UID with jobs.
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
mConnManager.registerNetworkCallback(request, mNetworkCallback);
-
- mNetPolicyManager.registerListener(mNetPolicyListener);
}
@GuardedBy("mLock")
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
if (jobStatus.hasConnectivityConstraint()) {
- updateConstraintsSatisfied(jobStatus);
+ UidStats uidStats = mUidStats.get(jobStatus.getSourceUid());
+ if (uidStats == null) {
+ uidStats = new UidStats(jobStatus.getSourceUid());
+ mUidStats.append(jobStatus.getSourceUid(), uidStats);
+ }
+ if (wouldBeReadyWithConstraintLocked(jobStatus, JobStatus.CONSTRAINT_CONNECTIVITY)) {
+ uidStats.numReadyWithConnectivity++;
+ }
ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid());
if (jobs == null) {
jobs = new ArraySet<>();
@@ -129,6 +203,16 @@
}
jobs.add(jobStatus);
jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY);
+ updateConstraintsSatisfied(jobStatus);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Override
+ public void prepareForExecutionLocked(JobStatus jobStatus) {
+ if (jobStatus.hasConnectivityConstraint()) {
+ UidStats uidStats = mUidStats.get(jobStatus.getSourceUid());
+ uidStats.numRunning++;
}
}
@@ -141,7 +225,13 @@
if (jobs != null) {
jobs.remove(jobStatus);
}
+ UidStats us = mUidStats.get(jobStatus.getSourceUid());
+ us.numReadyWithConnectivity--;
+ if (jobStatus.madeActive != 0) {
+ us.numRunning--;
+ }
maybeRevokeStandbyExceptionLocked(jobStatus);
+ maybeAdjustRegisteredCallbacksLocked();
}
}
@@ -229,6 +319,8 @@
return;
}
+ UidStats uidStats = mUidStats.get(jobStatus.getSourceUid());
+
if (jobStatus.shouldTreatAsExpeditedJob()) {
if (!jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY)) {
// Don't request a direct hole through any of the firewalls. Instead, mark the
@@ -252,11 +344,15 @@
if (DEBUG) {
Slog.i(TAG, "evaluateStateLocked finds job " + jobStatus + " would be ready.");
}
+ uidStats.numReadyWithConnectivity++;
requestStandbyExceptionLocked(jobStatus);
} else {
if (DEBUG) {
Slog.i(TAG, "evaluateStateLocked finds job " + jobStatus + " would not be ready.");
}
+ // Don't decrement numReadyWithConnectivity here because we don't know if it was
+ // incremented for this job. The count will be set properly in
+ // maybeAdjustRegisteredCallbacksLocked().
maybeRevokeStandbyExceptionLocked(jobStatus);
}
}
@@ -316,6 +412,30 @@
@Override
public void onAppRemovedLocked(String pkgName, int uid) {
mTrackedJobs.delete(uid);
+ UidStats uidStats = mUidStats.removeReturnOld(uid);
+ unregisterDefaultNetworkCallbackLocked(uid, sElapsedRealtimeClock.millis());
+ mSortedStats.remove(uidStats);
+ registerPendingUidCallbacksLocked();
+ }
+
+ @GuardedBy("mLock")
+ @Override
+ public void onUserRemovedLocked(int userId) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ for (int u = mUidStats.size() - 1; u >= 0; --u) {
+ UidStats uidStats = mUidStats.valueAt(u);
+ if (UserHandle.getUserId(uidStats.uid) == userId) {
+ unregisterDefaultNetworkCallbackLocked(uidStats.uid, nowElapsed);
+ mSortedStats.remove(uidStats);
+ mUidStats.removeAt(u);
+ }
+ }
+ maybeAdjustRegisteredCallbacksLocked();
+ }
+
+ private boolean isUsable(NetworkCapabilities capabilities) {
+ return capabilities != null
+ && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
}
/**
@@ -428,6 +548,8 @@
// Zeroth, we gotta have a network to think about being satisfied
if (network == null || capabilities == null) return false;
+ if (!isUsable(capabilities)) return false;
+
// First, are we insane?
if (isInsane(jobStatus, network, capabilities, constants)) return false;
@@ -443,6 +565,156 @@
return false;
}
+ @GuardedBy("mLock")
+ private void maybeRegisterDefaultNetworkCallbackLocked(JobStatus jobStatus) {
+ final int sourceUid = jobStatus.getSourceUid();
+ if (mCurrentDefaultNetworkCallbacks.contains(sourceUid)) {
+ return;
+ }
+ UidStats uidStats = mUidStats.get(sourceUid);
+ if (!mSortedStats.contains(uidStats)) {
+ mSortedStats.add(uidStats);
+ }
+ if (mCurrentDefaultNetworkCallbacks.size() >= MAX_NETWORK_CALLBACKS) {
+ // TODO: offload to handler
+ maybeAdjustRegisteredCallbacksLocked();
+ return;
+ }
+ registerPendingUidCallbacksLocked();
+ }
+
+ /**
+ * Register UID callbacks for UIDs that are next in line, based on the current order in {@link
+ * #mSortedStats}. This assumes that there are only registered callbacks for UIDs in the top
+ * {@value #MAX_NETWORK_CALLBACKS} UIDs and that the only UIDs missing callbacks are the lower
+ * priority ones.
+ */
+ @GuardedBy("mLock")
+ private void registerPendingUidCallbacksLocked() {
+ final int numCallbacks = mCurrentDefaultNetworkCallbacks.size();
+ final int numPending = mSortedStats.size();
+ if (numPending < numCallbacks) {
+ // This means there's a bug in the code >.<
+ Slog.wtf(TAG, "There are more registered callbacks than sorted UIDs: "
+ + numCallbacks + " vs " + numPending);
+ }
+ for (int i = numCallbacks; i < numPending && i < MAX_NETWORK_CALLBACKS; ++i) {
+ UidStats uidStats = mSortedStats.get(i);
+ UidDefaultNetworkCallback callback = mDefaultNetworkCallbackPool.acquire();
+ if (callback == null) {
+ callback = new UidDefaultNetworkCallback();
+ }
+ callback.setUid(uidStats.uid);
+ mCurrentDefaultNetworkCallbacks.append(uidStats.uid, callback);
+ mConnManager.registerDefaultNetworkCallbackAsUid(uidStats.uid, callback, mHandler);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void maybeAdjustRegisteredCallbacksLocked() {
+ final int count = mUidStats.size();
+ if (count == mCurrentDefaultNetworkCallbacks.size()) {
+ // All of them are registered and there are no blocked UIDs.
+ // No point evaluating all UIDs.
+ return;
+ }
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ mSortedStats.clear();
+
+ for (int u = 0; u < mUidStats.size(); ++u) {
+ UidStats us = mUidStats.valueAt(u);
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(us.uid);
+ if (jobs == null || jobs.size() == 0) {
+ unregisterDefaultNetworkCallbackLocked(us.uid, nowElapsed);
+ continue;
+ }
+
+ // We won't evaluate stats in the first 30 seconds after boot...That's probably okay.
+ if (us.lastUpdatedElapsed + MIN_STATS_UPDATE_INTERVAL_MS < nowElapsed) {
+ us.earliestEnqueueTime = Long.MAX_VALUE;
+ us.earliestEJEnqueueTime = Long.MAX_VALUE;
+ us.numReadyWithConnectivity = 0;
+ us.numRequestedNetworkAvailable = 0;
+ us.numRegular = 0;
+ us.numEJs = 0;
+
+ for (int j = 0; j < jobs.size(); ++j) {
+ JobStatus job = jobs.valueAt(j);
+ us.earliestEnqueueTime = Math.min(us.earliestEnqueueTime, job.enqueueTime);
+ if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_CONNECTIVITY)) {
+ us.numReadyWithConnectivity++;
+ if (isNetworkAvailable(job)) {
+ us.numRequestedNetworkAvailable++;
+ }
+ }
+ if (job.shouldTreatAsExpeditedJob() || job.startedAsExpeditedJob) {
+ us.numEJs++;
+ us.earliestEJEnqueueTime =
+ Math.min(us.earliestEJEnqueueTime, job.enqueueTime);
+ } else {
+ us.numRegular++;
+ }
+ }
+
+ us.lastUpdatedElapsed = nowElapsed;
+ }
+ mSortedStats.add(us);
+ }
+
+ mSortedStats.sort(mUidStatsComparator);
+
+ boolean changed = false;
+ // Iterate in reverse order to remove existing callbacks before adding new ones.
+ for (int i = mSortedStats.size() - 1; i >= 0; --i) {
+ UidStats us = mSortedStats.get(i);
+ if (i >= MAX_NETWORK_CALLBACKS) {
+ changed |= unregisterDefaultNetworkCallbackLocked(us.uid, nowElapsed);
+ } else {
+ UidDefaultNetworkCallback defaultNetworkCallback =
+ mCurrentDefaultNetworkCallbacks.get(us.uid);
+ if (defaultNetworkCallback == null) {
+ // Not already registered.
+ defaultNetworkCallback = mDefaultNetworkCallbackPool.acquire();
+ if (defaultNetworkCallback == null) {
+ defaultNetworkCallback = new UidDefaultNetworkCallback();
+ }
+ defaultNetworkCallback.setUid(us.uid);
+ mCurrentDefaultNetworkCallbacks.append(us.uid, defaultNetworkCallback);
+ mConnManager.registerDefaultNetworkCallbackAsUid(
+ us.uid, defaultNetworkCallback, mHandler);
+ }
+ }
+ }
+ if (changed) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean unregisterDefaultNetworkCallbackLocked(int uid, long nowElapsed) {
+ UidDefaultNetworkCallback defaultNetworkCallback = mCurrentDefaultNetworkCallbacks.get(uid);
+ if (defaultNetworkCallback == null) {
+ return false;
+ }
+ mCurrentDefaultNetworkCallbacks.remove(uid);
+ mConnManager.unregisterNetworkCallback(defaultNetworkCallback);
+ mDefaultNetworkCallbackPool.release(defaultNetworkCallback);
+ defaultNetworkCallback.clear();
+
+ boolean changed = false;
+ final ArraySet<JobStatus> jobs = mTrackedJobs.get(uid);
+ if (jobs != null) {
+ // Since we're unregistering the callback, we can no longer monitor
+ // changes to the app's network and so we should just mark the
+ // connectivity constraint as not satisfied.
+ for (int j = jobs.size() - 1; j >= 0; --j) {
+ changed |= updateConstraintsSatisfied(
+ jobs.valueAt(j), nowElapsed, null, null);
+ }
+ }
+ return changed;
+ }
+
@Nullable
private NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
if (network == null) {
@@ -451,43 +723,34 @@
synchronized (mLock) {
// There is technically a race here if the Network object is reused. This can happen
// only if that Network disconnects and the auto-incrementing network ID in
- // ConnectivityService wraps. This should no longer be a concern if/when we only make
+ // ConnectivityService wraps. This shouldn't be a concern since we only make
// use of asynchronous calls.
- if (mAvailableNetworks.get(network) != null) {
- return mAvailableNetworks.get(network);
- }
-
- // This should almost never happen because any time a new network connects, the
- // NetworkCallback would populate mAvailableNetworks. However, it's currently necessary
- // because we also call synchronous methods such as getActiveNetworkForUid.
- // TODO(134978280): remove after switching to callback-based APIs
- final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
- mAvailableNetworks.put(network, capabilities);
- return capabilities;
+ return mAvailableNetworks.get(network);
}
}
private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
- final Network network = mConnManager.getActiveNetworkForUid(
- jobStatus.getSourceUid(), jobStatus.shouldIgnoreNetworkBlocking());
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ final UidDefaultNetworkCallback defaultNetworkCallback =
+ mCurrentDefaultNetworkCallbacks.get(jobStatus.getSourceUid());
+ if (defaultNetworkCallback == null) {
+ maybeRegisterDefaultNetworkCallbackLocked(jobStatus);
+ return updateConstraintsSatisfied(jobStatus, nowElapsed, null, null);
+ }
+ final Network network =
+ (jobStatus.shouldIgnoreNetworkBlocking() || !defaultNetworkCallback.mBlocked)
+ ? defaultNetworkCallback.mDefaultNetwork : null;
final NetworkCapabilities capabilities = getNetworkCapabilities(network);
- return updateConstraintsSatisfied(jobStatus, sElapsedRealtimeClock.millis(),
- network, capabilities);
+ return updateConstraintsSatisfied(jobStatus, nowElapsed, network, capabilities);
}
private boolean updateConstraintsSatisfied(JobStatus jobStatus, final long nowElapsed,
Network network, NetworkCapabilities capabilities) {
- // TODO: consider matching against non-active networks
+ // TODO: consider matching against non-default networks
- final boolean ignoreBlocked = jobStatus.shouldIgnoreNetworkBlocking();
- final NetworkInfo info = mConnManager.getNetworkInfoForUid(network,
- jobStatus.getSourceUid(), ignoreBlocked);
-
- final boolean connected = (info != null) && info.isConnected();
final boolean satisfied = isSatisfied(jobStatus, network, capabilities, mConstants);
- final boolean changed = jobStatus
- .setConnectivityConstraintSatisfied(nowElapsed, connected && satisfied);
+ final boolean changed = jobStatus.setConnectivityConstraintSatisfied(nowElapsed, satisfied);
// Pass along the evaluated network for job to use; prevents race
// conditions as default routes change over time, and opens the door to
@@ -496,7 +759,7 @@
if (DEBUG) {
Slog.i(TAG, "Connectivity " + (changed ? "CHANGED" : "unchanged")
- + " for " + jobStatus + ": connected=" + connected
+ + " for " + jobStatus + ": usable=" + isUsable(capabilities)
+ " satisfied=" + satisfied);
}
return changed;
@@ -531,15 +794,24 @@
return false;
}
- final Network network =
- mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid(), false);
+ UidDefaultNetworkCallback defaultNetworkCallback =
+ mCurrentDefaultNetworkCallbacks.get(jobs.valueAt(0).getSourceUid());
+ if (defaultNetworkCallback == null) {
+ maybeRegisterDefaultNetworkCallbackLocked(jobs.valueAt(0));
+ return false;
+ }
+
+ final Network network = defaultNetworkCallback.mBlocked
+ ? null : defaultNetworkCallback.mDefaultNetwork;
final NetworkCapabilities capabilities = getNetworkCapabilities(network);
final boolean networkMatch = (filterNetwork == null
|| Objects.equals(filterNetwork, network));
- boolean exemptedLoaded = false;
- Network exemptedNetwork = null;
- NetworkCapabilities exemptedNetworkCapabilities = null;
- boolean exemptedNetworkMatch = false;
+ // Ignore blocked
+ final Network exemptedNetwork = defaultNetworkCallback.mDefaultNetwork;
+ final NetworkCapabilities exemptedNetworkCapabilities =
+ getNetworkCapabilities(exemptedNetwork);
+ final boolean exemptedNetworkMatch =
+ (filterNetwork == null || Objects.equals(filterNetwork, exemptedNetwork));
final long nowElapsed = sElapsedRealtimeClock.millis();
boolean changed = false;
@@ -551,13 +823,6 @@
boolean match = networkMatch;
if (js.shouldIgnoreNetworkBlocking()) {
- if (!exemptedLoaded) {
- exemptedLoaded = true;
- exemptedNetwork = mConnManager.getActiveNetworkForUid(js.getSourceUid(), true);
- exemptedNetworkCapabilities = getNetworkCapabilities(exemptedNetwork);
- exemptedNetworkMatch = (filterNetwork == null
- || Objects.equals(filterNetwork, exemptedNetwork));
- }
net = exemptedNetwork;
netCap = exemptedNetworkCapabilities;
match = exemptedNetworkMatch;
@@ -612,6 +877,7 @@
mAvailableNetworks.put(network, capabilities);
}
updateTrackedJobs(-1, network);
+ maybeAdjustRegisteredCallbacksLocked();
}
@Override
@@ -621,26 +887,15 @@
}
synchronized (mLock) {
mAvailableNetworks.remove(network);
+ for (int u = 0; u < mCurrentDefaultNetworkCallbacks.size(); ++u) {
+ UidDefaultNetworkCallback callback = mCurrentDefaultNetworkCallbacks.valueAt(u);
+ if (Objects.equals(callback.mDefaultNetwork, network)) {
+ callback.mDefaultNetwork = null;
+ }
+ }
}
updateTrackedJobs(-1, network);
- }
- };
-
- private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() {
- @Override
- public void onRestrictBackgroundChanged(boolean restrictBackground) {
- if (DEBUG) {
- Slog.v(TAG, "onRestrictBackgroundChanged: " + restrictBackground);
- }
- mHandler.obtainMessage(MSG_DATA_SAVER_TOGGLED).sendToTarget();
- }
-
- @Override
- public void onUidRulesChanged(int uid, int uidRules) {
- if (DEBUG) {
- Slog.v(TAG, "onUidRulesChanged: " + uid);
- }
- mHandler.obtainMessage(MSG_UID_RULES_CHANGES, uid, 0).sendToTarget();
+ maybeAdjustRegisteredCallbacksLocked();
}
};
@@ -653,24 +908,148 @@
public void handleMessage(Message msg) {
synchronized (mLock) {
switch (msg.what) {
- case MSG_DATA_SAVER_TOGGLED:
- updateTrackedJobs(-1, null);
- break;
- case MSG_UID_RULES_CHANGES:
- updateTrackedJobs(msg.arg1, null);
- break;
case MSG_REEVALUATE_JOBS:
updateTrackedJobs(-1, null);
break;
}
}
}
- };
+ }
+
+ private class UidDefaultNetworkCallback extends NetworkCallback {
+ private int mUid;
+ @Nullable
+ private Network mDefaultNetwork;
+ private boolean mBlocked;
+
+ private void setUid(int uid) {
+ mUid = uid;
+ mDefaultNetwork = null;
+ }
+
+ private void clear() {
+ mDefaultNetwork = null;
+ mUid = UserHandle.USER_NULL;
+ }
+
+ @Override
+ public void onAvailable(Network network) {
+ if (DEBUG) Slog.v(TAG, "default-onAvailable(" + mUid + "): " + network);
+ }
+
+ @Override
+ public void onBlockedStatusChanged(Network network, boolean blocked) {
+ if (DEBUG) {
+ Slog.v(TAG, "default-onBlockedStatusChanged(" + mUid + "): "
+ + network + " -> " + blocked);
+ }
+ if (mUid == UserHandle.USER_NULL) {
+ return;
+ }
+ synchronized (mLock) {
+ mDefaultNetwork = network;
+ mBlocked = blocked;
+ }
+ updateTrackedJobs(mUid, network);
+ }
+
+ // Network transitions have some complicated behavior that JS doesn't handle very well.
+ //
+ // * If the default network changes from A to B without A disconnecting, then we'll only
+ // get onAvailable(B) (and the subsequent onBlockedStatusChanged() call). Since we get
+ // the onBlockedStatusChanged() call, we re-evaluate the job, but keep it running
+ // (assuming the new network satisfies constraints). The app continues to use the old
+ // network (if they use the network object provided through JobParameters.getNetwork())
+ // because we don't notify them of the default network change. If the old network no
+ // longer satisfies requested constraints, then we have a problem. Depending on the order
+ // of calls, if the per-UID callback gets notified of the network change before the
+ // general callback gets notified of the capabilities change, then the job's network
+ // object will point to the new network and we won't stop the job, even though we told it
+ // to use the old network that no longer satisfies its constraints. This is the behavior
+ // we loosely had (ignoring race conditions between asynchronous and synchronous
+ // connectivity calls) when we were calling the synchronous getActiveNetworkForUid() API.
+ // However, we should fix it.
+ // TODO: stop jobs when the existing capabilities change after default network change
+ //
+ // * If the default network changes from A to B because A disconnected, then we'll get
+ // onLost(A) and then onAvailable(B). In this case, there will be a short period where JS
+ // doesn't think there's an available network for the job, so we'll stop the job even
+ // though onAvailable(B) will be called soon. One on hand, the app would have gotten a
+ // network error as well because of A's disconnect, and this will allow JS to provide the
+ // job with the new default network. On the other hand, we have to stop the job even
+ // though it could have continued running with the new network and the job has to deal
+ // with whatever backoff policy is set. For now, the current behavior is fine, but we may
+ // want to see if there's a way to have a smoother transition.
+
+ @Override
+ public void onLost(Network network) {
+ if (DEBUG) {
+ Slog.v(TAG, "default-onLost(" + mUid + "): " + network);
+ }
+ if (mUid == UserHandle.USER_NULL) {
+ return;
+ }
+ synchronized (mLock) {
+ if (Objects.equals(mDefaultNetwork, network)) {
+ mDefaultNetwork = null;
+ }
+ }
+ updateTrackedJobs(mUid, network);
+ }
+
+ private void dumpLocked(IndentingPrintWriter pw) {
+ pw.print("UID: ");
+ pw.print(mUid);
+ pw.print("; ");
+ if (mDefaultNetwork == null) {
+ pw.print("No network");
+ } else {
+ pw.print("Network: ");
+ pw.print(mDefaultNetwork);
+ pw.print(" (blocked=");
+ pw.print(mBlocked);
+ pw.print(")");
+ }
+ pw.println();
+ }
+ }
+
+ private static class UidStats {
+ public final int uid;
+ public int numRunning;
+ public int numReadyWithConnectivity;
+ public int numRequestedNetworkAvailable;
+ public int numEJs;
+ public int numRegular;
+ public long earliestEnqueueTime;
+ public long earliestEJEnqueueTime;
+ public long lastUpdatedElapsed;
+
+ private UidStats(int uid) {
+ this.uid = uid;
+ }
+
+ private void dumpLocked(IndentingPrintWriter pw, final long nowElapsed) {
+ pw.print("UidStats{");
+ pw.print("uid", uid);
+ pw.print("#run", numRunning);
+ pw.print("#readyWithConn", numReadyWithConnectivity);
+ pw.print("#netAvail", numRequestedNetworkAvailable);
+ pw.print("#EJs", numEJs);
+ pw.print("#reg", numRegular);
+ pw.print("earliestEnqueue", earliestEnqueueTime);
+ pw.print("earliestEJEnqueue", earliestEJEnqueueTime);
+ pw.print("updated=");
+ TimeUtils.formatDuration(lastUpdatedElapsed - nowElapsed, pw);
+ pw.println("}");
+ }
+ }
@GuardedBy("mLock")
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
if (mRequestedWhitelistJobs.size() > 0) {
pw.print("Requested standby exceptions:");
@@ -695,6 +1074,26 @@
} else {
pw.println("No available networks");
}
+ pw.println();
+
+ pw.println("Current default network callbacks:");
+ pw.increaseIndent();
+ for (int i = 0; i < mCurrentDefaultNetworkCallbacks.size(); i++) {
+ mCurrentDefaultNetworkCallbacks.valueAt(i).dumpLocked(pw);
+ }
+ pw.decreaseIndent();
+ pw.println();
+
+ pw.println("UID Pecking Order:");
+ pw.increaseIndent();
+ for (int i = 0; i < mSortedStats.size(); ++i) {
+ pw.print(i);
+ pw.print(": ");
+ mSortedStats.get(i).dumpLocked(pw, nowElapsed);
+ }
+ pw.decreaseIndent();
+ pw.println();
+
for (int i = 0; i < mTrackedJobs.size(); i++) {
final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
for (int j = 0; j < jobs.size(); j++) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 8d999e1..80e68e9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -142,13 +142,14 @@
* (Atom #21)
* * CONSTRAINT_BACKGROUND_NOT_RESTRICTED can be inferred with BatterySaverModeStateChanged
* (Atom #20)
+ * * CONSTRAINT_STORAGE_NOT_LOW can be inferred with LowStorageStateChanged (Atom #130)
*/
private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER
| CONSTRAINT_DEADLINE
| CONSTRAINT_IDLE
- | CONSTRAINT_STORAGE_NOT_LOW
| CONSTRAINT_TIMING_DELAY
- | CONSTRAINT_WITHIN_QUOTA;
+ | CONSTRAINT_WITHIN_QUOTA
+ | CONSTRAINT_WITHIN_EXPEDITED_QUOTA;
// TODO(b/129954980)
private static final boolean STATS_LOG_ENABLED = false;
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 20ce133..3cf585e 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -41,9 +41,7 @@
installable: true,
sdk_version: "module_current",
- libs: [
- "framework_media_annotation",
- ],
+ libs: ["framework-annotations-lib"],
static_libs: [
"exoplayer2-extractor",
"mediatranscoding_aidl_interface-java",
@@ -138,20 +136,9 @@
api_lint: {
enabled: false,
},
-
- libs: [
- "framework_media_annotation",
- ],
impl_library_visibility: ["//frameworks/av/apex:__subpackages__"],
}
-java_library {
- name: "framework_media_annotation",
- srcs: [":framework-media-annotation-srcs"],
- installable: false,
- sdk_version: "core_current",
-}
-
cc_library_shared {
name: "libmediaparser-jni",
srcs: [
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index ad68169..8d83309 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -25,7 +25,9 @@
}
public static final class MediaTranscodeManager.TranscodingSession {
+ method public void addClientUid(int);
method public void cancel();
+ method @NonNull public java.util.List<java.lang.Integer> getClientUids();
method public int getErrorCode();
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index 7f4685e..fef4865 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -1352,6 +1352,8 @@
private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
@GuardedBy("mLock")
private boolean mHasRetried = false;
+ @GuardedBy("mLock")
+ private @NonNull List<Integer> mClientUidList = new ArrayList<>();
// The original request that associated with this session.
private final TranscodingRequest mRequest;
@@ -1370,6 +1372,7 @@
mListenerExecutor = executor;
mListener = listener;
mRequest = request;
+ mClientUidList.add(request.getClientUid());
}
/**
@@ -1515,6 +1518,36 @@
}
/**
+ * Adds a client uid that is also waiting for this transcoding session.
+ * <p>
+ * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could add the
+ * uid. Note that the permission check happens on the service side upon starting the
+ * transcoding. If the client does not have the permission, the transcoding will fail.
+ */
+ public void addClientUid(int uid) {
+ if (uid < 0) {
+ throw new IllegalArgumentException("Invalid Uid");
+ }
+ synchronized (mLock) {
+ if (!mClientUidList.contains(uid)) {
+ // see ag/14023202 for implementation
+ mClientUidList.add(uid);
+ }
+ }
+ }
+
+ /**
+ * Query all the client that waiting for this transcoding session
+ * @return a list containing all the client uids.
+ */
+ @NonNull
+ public List<Integer> getClientUids() {
+ synchronized (mLock) {
+ return mClientUidList;
+ }
+ }
+
+ /**
* Gets sessionId of the transcoding session.
* @return session id.
*/
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index ca1d598..f0ac530 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -19,6 +19,7 @@
import android.app.ActivityManager;
import android.app.ContentProviderHolder;
import android.app.IActivityManager;
+import android.content.AttributionSource;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.IContentProvider;
@@ -562,7 +563,8 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.insert(resolveCallingPackage(), null, mUri, mContentValues, mExtras);
+ provider.insert(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri, mContentValues, mExtras);
}
}
@@ -576,7 +578,8 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.delete(resolveCallingPackage(), null, mUri, mExtras);
+ provider.delete(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri, mExtras);
}
}
@@ -593,7 +596,8 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Bundle result = provider.call(null, null, mUri.getAuthority(), mMethod, mArg, mExtras);
+ Bundle result = provider.call(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri.getAuthority(), mMethod, mArg, mExtras);
if (result != null) {
result.size(); // unpack
}
@@ -620,7 +624,9 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "r", null, null)) {
+ try (ParcelFileDescriptor fd = provider.openFile(
+ new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri, "r", null)) {
FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
}
}
@@ -633,7 +639,8 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "w", null, null)) {
+ try (ParcelFileDescriptor fd = provider.openFile(new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), null), mUri, "w", null)) {
FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
}
}
@@ -651,8 +658,8 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Cursor cursor = provider.query(resolveCallingPackage(), null, mUri, mProjection,
- mExtras, null);
+ Cursor cursor = provider.query(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri, mProjection, mExtras, null);
if (cursor == null) {
System.out.println("No result found.");
return;
@@ -716,7 +723,8 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.update(resolveCallingPackage(), null, mUri, mValues, mExtras);
+ provider.update(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), mUri, mValues, mExtras);
}
}
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index b23bf5d..bc95986 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -20,6 +20,7 @@
import android.app.ContentProviderHolder;
import android.app.IActivityManager;
import android.app.UiAutomation;
+import android.content.AttributionSource;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentProvider;
@@ -28,6 +29,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -67,7 +69,8 @@
throw new IllegalStateException("Could not find provider: " + providerName);
}
provider = holder.provider;
- cursor = provider.query(null, null, Settings.Secure.CONTENT_URI,
+ cursor = provider.query(new AttributionSource(Binder.getCallingUid(),
+ resolveCallingPackage(), null), Settings.Secure.CONTENT_URI,
new String[] {
Settings.Secure.VALUE
},
@@ -123,4 +126,18 @@
}
return ret;
}
+
+ private static String resolveCallingPackage() {
+ switch (Binder.getCallingUid()) {
+ case Process.ROOT_UID: {
+ return "root";
+ }
+ case Process.SHELL_UID: {
+ return "com.android.shell";
+ }
+ default: {
+ return null;
+ }
+ }
+ }
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 6abb3f7..f8654478 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -168,6 +168,7 @@
field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
field public static final String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
+ field public static final String UPDATE_PACKAGES_WITHOUT_USER_ACTION = "android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION";
field public static final String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC";
field @Deprecated public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
field public static final String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT";
@@ -866,6 +867,7 @@
field public static final int keycode = 16842949; // 0x10100c5
field public static final int killAfterRestore = 16843420; // 0x101029c
field public static final int knownCerts = 16844330; // 0x101062a
+ field public static final int lStar = 16844359; // 0x1010647
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
field @Deprecated public static final int labelTextSize = 16843317; // 0x1010235
@@ -1009,7 +1011,7 @@
field public static final int multiArch = 16843918; // 0x101048e
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
- field public static final int nativeHeapZeroInit = 16844325; // 0x1010625
+ field public static final int nativeHeapZeroInitialized = 16844325; // 0x1010625
field public static final int navigationBarColor = 16843858; // 0x1010452
field public static final int navigationBarDividerColor = 16844141; // 0x101056d
field public static final int navigationContentDescription = 16843969; // 0x10104c1
@@ -5715,6 +5717,9 @@
field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
field @Deprecated public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+ field public static final int FOREGROUND_SERVICE_DEFAULT = 0; // 0x0
+ field public static final int FOREGROUND_SERVICE_DEFERRED = 2; // 0x2
+ field public static final int FOREGROUND_SERVICE_IMMEDIATE = 1; // 0x1
field public static final int GROUP_ALERT_ALL = 0; // 0x0
field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
@@ -5918,6 +5923,7 @@
method @NonNull public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
method @NonNull public android.app.Notification.Builder setExtras(android.os.Bundle);
method @NonNull public android.app.Notification.Builder setFlag(int, boolean);
+ method @NonNull public android.app.Notification.Builder setForegroundServiceBehavior(int);
method @NonNull public android.app.Notification.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
method @NonNull public android.app.Notification.Builder setGroup(String);
method @NonNull public android.app.Notification.Builder setGroupAlertBehavior(int);
@@ -5936,7 +5942,7 @@
method @NonNull public android.app.Notification.Builder setRemoteInputHistory(CharSequence[]);
method @NonNull public android.app.Notification.Builder setSettingsText(CharSequence);
method @NonNull public android.app.Notification.Builder setShortcutId(String);
- method @NonNull public android.app.Notification.Builder setShowForegroundImmediately(boolean);
+ method @Deprecated @NonNull public android.app.Notification.Builder setShowForegroundImmediately(boolean);
method @NonNull public android.app.Notification.Builder setShowWhen(boolean);
method @NonNull public android.app.Notification.Builder setSmallIcon(@DrawableRes int);
method @NonNull public android.app.Notification.Builder setSmallIcon(@DrawableRes int, int);
@@ -7013,6 +7019,7 @@
method public void onBugreportShared(@NonNull android.content.Context, @NonNull android.content.Intent, @NonNull String);
method public void onBugreportSharingDeclined(@NonNull android.content.Context, @NonNull android.content.Intent);
method @Nullable public String onChoosePrivateKeyAlias(@NonNull android.content.Context, @NonNull android.content.Intent, int, @Nullable android.net.Uri, @Nullable String);
+ method public void onComplianceAcknowledgementRequired(@NonNull android.content.Context, @NonNull android.content.Intent);
method @Nullable public CharSequence onDisableRequested(@NonNull android.content.Context, @NonNull android.content.Intent);
method public void onDisabled(@NonNull android.content.Context, @NonNull android.content.Intent);
method public void onEnabled(@NonNull android.content.Context, @NonNull android.content.Intent);
@@ -7067,6 +7074,7 @@
}
public class DevicePolicyManager {
+ method public void acknowledgeDeviceCompliant();
method public void addCrossProfileIntentFilter(@NonNull android.content.ComponentName, android.content.IntentFilter, int);
method public boolean addCrossProfileWidgetProvider(@NonNull android.content.ComponentName, String);
method public int addOverrideApn(@NonNull android.content.ComponentName, @NonNull android.telephony.data.ApnSetting);
@@ -7126,6 +7134,8 @@
method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName);
method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
method @NonNull public java.util.List<java.lang.String> getMeteredDataDisabledPackages(@NonNull android.content.ComponentName);
+ method public int getNearbyAppStreamingPolicy();
+ method public int getNearbyNotificationStreamingPolicy();
method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName);
method @Nullable public CharSequence getOrganizationName(@NonNull android.content.ComponentName);
method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(@NonNull android.content.ComponentName);
@@ -7185,6 +7195,7 @@
method public boolean isBackupServiceEnabled(@NonNull android.content.ComponentName);
method @Deprecated public boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isCommonCriteriaModeEnabled(@Nullable android.content.ComponentName);
+ method public boolean isComplianceAcknowledgementRequired();
method public boolean isDeviceIdAttestationSupported();
method public boolean isDeviceOwnerApp(String);
method public boolean isEnterpriseNetworkPreferenceEnabled();
@@ -7268,6 +7279,8 @@
method public void setMaximumFailedPasswordsForWipe(@NonNull android.content.ComponentName, int);
method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long);
method @NonNull public java.util.List<java.lang.String> setMeteredDataDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
+ method public void setNearbyAppStreamingPolicy(int);
+ method public void setNearbyNotificationStreamingPolicy(int);
method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean);
method @Deprecated public void setOrganizationColor(@NonNull android.content.ComponentName, int);
method public void setOrganizationId(@NonNull String);
@@ -7436,6 +7449,9 @@
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
+ field public static final int NEARBY_STREAMING_DISABLED = 0; // 0x0
+ field public static final int NEARBY_STREAMING_ENABLED = 1; // 0x1
+ field public static final int NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY = 2; // 0x2
field public static final int OPERATION_SAFETY_REASON_DRIVING_DISTRACTION = 1; // 0x1
field public static final int PASSWORD_COMPLEXITY_HIGH = 327680; // 0x50000
field public static final int PASSWORD_COMPLEXITY_LOW = 65536; // 0x10000
@@ -7865,8 +7881,10 @@
public static class BlobStoreManager.Session implements java.io.Closeable {
method public void abandon() throws java.io.IOException;
method public void allowPackageAccess(@NonNull String, @NonNull byte[]) throws java.io.IOException;
+ method public void allowPackagesWithLocationPermission(@NonNull String) throws java.io.IOException;
method public void allowPublicAccess() throws java.io.IOException;
method public void allowSameSignatureAccess() throws java.io.IOException;
+ method public boolean arePackagesWithLocationPermissionAllowed(@NonNull String) throws java.io.IOException;
method public void close() throws java.io.IOException;
method public void commit(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws java.io.IOException;
method public long getSize() throws java.io.IOException;
@@ -9898,6 +9916,27 @@
method @Deprecated public void setUpdateThrottle(long);
}
+ public final class AttributionSource implements android.os.Parcelable {
+ method public boolean checkCallingUid();
+ method public int describeContents();
+ method public void enforceCallingUid();
+ method @Nullable public String getAttributionTag();
+ method @Nullable public android.content.AttributionSource getNext();
+ method @Nullable public String getPackageName();
+ method public int getUid();
+ method public boolean isTrusted(@NonNull android.content.Context);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.AttributionSource> CREATOR;
+ }
+
+ public static final class AttributionSource.Builder {
+ ctor public AttributionSource.Builder(int);
+ method @NonNull public android.content.AttributionSource build();
+ method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@NonNull String);
+ method @NonNull public android.content.AttributionSource.Builder setNext(@NonNull android.content.AttributionSource);
+ method @NonNull public android.content.AttributionSource.Builder setPackageName(@NonNull String);
+ }
+
public abstract class BroadcastReceiver {
ctor public BroadcastReceiver();
method public final void abortBroadcast();
@@ -10067,6 +10106,7 @@
method public abstract int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]);
method public int delete(@NonNull android.net.Uri, @Nullable android.os.Bundle);
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
+ method @Nullable public final android.content.AttributionSource getCallingAttributionSource();
method @Nullable public final String getCallingAttributionTag();
method @Nullable public final String getCallingPackage();
method @Nullable public final String getCallingPackageUnchecked();
@@ -10429,6 +10469,7 @@
method public abstract android.content.Context getApplicationContext();
method public abstract android.content.pm.ApplicationInfo getApplicationInfo();
method public abstract android.content.res.AssetManager getAssets();
+ method @NonNull public android.content.AttributionSource getAttributionSource();
method @Nullable public String getAttributionTag();
method public abstract java.io.File getCacheDir();
method public abstract ClassLoader getClassLoader();
@@ -10634,8 +10675,7 @@
public final class ContextParams {
method @Nullable public String getAttributionTag();
- method @Nullable public String getReceiverAttributionTag();
- method @Nullable public String getReceiverPackage();
+ method @Nullable public android.content.AttributionSource getNextAttributionSource();
}
public static final class ContextParams.Builder {
@@ -10643,7 +10683,7 @@
ctor public ContextParams.Builder(@NonNull android.content.ContextParams);
method @NonNull public android.content.ContextParams build();
method @NonNull public android.content.ContextParams.Builder setAttributionTag(@Nullable String);
- method @NonNull public android.content.ContextParams.Builder setReceiverPackage(@Nullable String, @Nullable String);
+ method @NonNull public android.content.ContextParams.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
}
public class ContextWrapper extends android.content.Context {
@@ -11864,7 +11904,7 @@
method public static CharSequence getCategoryTitle(android.content.Context, int);
method public int getGwpAsanMode();
method public int getMemtagMode();
- method @Nullable public Boolean isNativeHeapZeroInit();
+ method public int getNativeHeapZeroInitialized();
method public boolean isProfileableByShell();
method public boolean isResourceOverlay();
method public boolean isVirtualPreload();
@@ -11919,6 +11959,9 @@
field public static final int MEMTAG_DEFAULT = -1; // 0xffffffff
field public static final int MEMTAG_OFF = 0; // 0x0
field public static final int MEMTAG_SYNC = 2; // 0x2
+ field public static final int ZEROINIT_DEFAULT = -1; // 0xffffffff
+ field public static final int ZEROINIT_DISABLED = 0; // 0x0
+ field public static final int ZEROINIT_ENABLED = 1; // 0x1
field public String appComponentFactory;
field public String backupAgentName;
field public int category;
@@ -12079,6 +12122,14 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstallSourceInfo> CREATOR;
}
+ public final class InstallationFile {
+ method public long getLengthBytes();
+ method public int getLocation();
+ method @Nullable public byte[] getMetadata();
+ method @NonNull public String getName();
+ method @Nullable public byte[] getSignature();
+ }
+
public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
ctor public InstrumentationInfo();
ctor public InstrumentationInfo(android.content.pm.InstrumentationInfo);
@@ -12133,6 +12184,7 @@
method @Nullable public android.content.IntentSender getShortcutConfigActivityIntent(@NonNull android.content.pm.LauncherActivityInfo);
method public java.util.List<android.content.pm.LauncherActivityInfo> getShortcutConfigActivityList(@Nullable String, @NonNull android.os.UserHandle);
method public android.graphics.drawable.Drawable getShortcutIconDrawable(@NonNull android.content.pm.ShortcutInfo, int);
+ method @Nullable public android.app.PendingIntent getShortcutIntent(@NonNull String, @NonNull String, @Nullable android.os.Bundle, @NonNull android.os.UserHandle);
method @Nullable public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(@NonNull android.content.pm.LauncherApps.ShortcutQuery, @NonNull android.os.UserHandle);
method @Nullable public android.os.Bundle getSuspendedPackageLauncherExtras(String, android.os.UserHandle);
method public boolean hasShortcutHostPermission();
@@ -12316,6 +12368,7 @@
method @NonNull public java.io.InputStream openRead(@NonNull String) throws java.io.IOException;
method @NonNull public java.io.OutputStream openWrite(@NonNull String, long, long) throws java.io.IOException;
method public void removeChildSessionId(int);
+ method public void removeFile(int, @NonNull String);
method public void removeSplit(@NonNull String) throws java.io.IOException;
method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException;
method public void setStagingProgress(float);
@@ -12349,6 +12402,7 @@
method public int getParentSessionId();
method public float getProgress();
method @Nullable public android.net.Uri getReferrerUri();
+ method public int getRequireUserAction();
method public int getSessionId();
method public long getSize();
method public int getStagedSessionErrorCode();
@@ -12373,6 +12427,9 @@
field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
+ field public static final int USER_ACTION_NOT_REQUIRED = 2; // 0x2
+ field public static final int USER_ACTION_REQUIRED = 1; // 0x1
+ field public static final int USER_ACTION_UNSPECIFIED = 0; // 0x0
}
public static class PackageInstaller.SessionParams implements android.os.Parcelable {
@@ -12390,6 +12447,7 @@
method public void setOriginatingUid(int);
method public void setOriginatingUri(@Nullable android.net.Uri);
method public void setReferrerUri(@Nullable android.net.Uri);
+ method public void setRequireUserAction(boolean);
method public void setSize(long);
method public void setWhitelistedRestrictedPermissions(@Nullable java.util.Set<java.lang.String>);
method public void writeToParcel(android.os.Parcel, int);
@@ -12601,6 +12659,7 @@
field public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
field public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
+ field public static final String FEATURE_HARDWARE_KEYSTORE = "android.hardware.hardware_keystore";
field public static final String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final String FEATURE_HOME_SCREEN = "android.software.home_screen";
field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE = "android.hardware.identity_credential";
@@ -13151,6 +13210,7 @@
method public boolean isStateful();
method @NonNull public static android.content.res.ColorStateList valueOf(@ColorInt int);
method @NonNull public android.content.res.ColorStateList withAlpha(int);
+ method @NonNull public android.content.res.ColorStateList withLStar(float);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.res.ColorStateList> CREATOR;
}
@@ -13373,7 +13433,8 @@
method public void setTo(android.content.res.Resources.Theme);
}
- public class TypedArray {
+ public class TypedArray implements java.lang.AutoCloseable {
+ method public void close();
method public boolean getBoolean(@StyleableRes int, boolean);
method public int getChangingConfigurations();
method @ColorInt public int getColor(@StyleableRes int, @ColorInt int);
@@ -17859,6 +17920,7 @@
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.String> INFO_VERSION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_DISTORTION;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_DISTORTION_MAXIMUM_RESOLUTION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_FACING;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_APERTURES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INFO_AVAILABLE_FILTER_DENSITIES;
@@ -17868,6 +17930,7 @@
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_HYPERFOCAL_DISTANCE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INTRINSIC_CALIBRATION;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_POSE_REFERENCE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION;
@@ -17887,9 +17950,11 @@
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_BLACK_LEVEL_PATTERN;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_CALIBRATION_TRANSFORM1;
@@ -17899,13 +17964,17 @@
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_FORWARD_MATRIX1;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.ColorSpaceTransform> SENSOR_FORWARD_MATRIX2;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SENSOR_INFO_BINNING_FACTOR;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_COLOR_FILTER_ARRANGEMENT;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Long>> SENSOR_INFO_EXPOSURE_TIME_RANGE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> SENSOR_INFO_LENS_SHADING_APPLIED;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Long> SENSOR_INFO_MAX_FRAME_DURATION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.SizeF> SENSOR_INFO_PHYSICAL_SIZE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect> SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect> SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> SENSOR_INFO_SENSITIVITY_RANGE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_TIMESTAMP_SOURCE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_WHITE_LEVEL;
@@ -18202,8 +18271,10 @@
field public static final int REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING = 4; // 0x4
field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING = 17; // 0x11
field public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13; // 0xd
field public static final int REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14; // 0xe
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR = 16; // 0x10
field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
@@ -18221,6 +18292,8 @@
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB = 0; // 0x0
field public static final int SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME = 1; // 0x1
field public static final int SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN = 0; // 0x0
+ field public static final int SENSOR_PIXEL_MODE_DEFAULT = 0; // 0x0
+ field public static final int SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION = 1; // 0x1
field public static final int SENSOR_REFERENCE_ILLUMINANT1_CLOUDY_WEATHER = 10; // 0xa
field public static final int SENSOR_REFERENCE_ILLUMINANT1_COOL_WHITE_FLUORESCENT = 14; // 0xe
field public static final int SENSOR_REFERENCE_ILLUMINANT1_D50 = 23; // 0x17
@@ -18350,6 +18423,7 @@
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> SCALER_ROTATE_AND_CROP;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_FRAME_DURATION;
+ field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> SENSOR_PIXEL_MODE;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> SENSOR_SENSITIVITY;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<int[]> SENSOR_TEST_PATTERN_DATA;
field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> SENSOR_TEST_PATTERN_MODE;
@@ -18453,6 +18527,8 @@
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> SENSOR_GREEN_SPLIT;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.util.Rational[]> SENSOR_NEUTRAL_COLOR_POINT;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.util.Pair<java.lang.Double,java.lang.Double>[]> SENSOR_NOISE_PROFILE;
+ field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> SENSOR_PIXEL_MODE;
+ field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> SENSOR_RAW_BINNING_FACTOR_USED;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_ROLLING_SHUTTER_SKEW;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> SENSOR_SENSITIVITY;
field @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> SENSOR_TEST_PATTERN_DATA;
@@ -18582,6 +18658,7 @@
method @NonNull public java.util.List<android.util.Size> getAvailableSizes();
method public int getFormat();
method public boolean isInput();
+ method public boolean isUltraHighResolution();
}
public final class MeteringRectangle {
@@ -18627,6 +18704,7 @@
ctor public OutputConfiguration(@NonNull android.view.Surface);
ctor public OutputConfiguration(int, @NonNull android.view.Surface);
ctor public OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
+ method public void addSensorPixelModeUsed(int);
method public void addSurface(@NonNull android.view.Surface);
method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
method public int describeContents();
@@ -18635,6 +18713,7 @@
method @Nullable public android.view.Surface getSurface();
method public int getSurfaceGroupId();
method @NonNull public java.util.List<android.view.Surface> getSurfaces();
+ method public void removeSensorPixelModeUsed(int);
method public void removeSurface(@NonNull android.view.Surface);
method public void setPhysicalCameraId(@Nullable String);
method public void writeToParcel(android.os.Parcel, int);
@@ -18763,9 +18842,14 @@
method public android.view.Display getDisplay(int);
method public android.view.Display[] getDisplays();
method public android.view.Display[] getDisplays(String);
+ method public int getMatchContentFrameRateUserPreference();
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+ field public static final int MATCH_CONTENT_FRAMERATE_ALWAYS = 2; // 0x2
+ field public static final int MATCH_CONTENT_FRAMERATE_NEVER = 0; // 0x0
+ field public static final int MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY = 1; // 0x1
+ field public static final int MATCH_CONTENT_FRAMERATE_UNKNOWN = -1; // 0xffffffff
field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
@@ -19805,7 +19889,8 @@
method public boolean hasSpeed();
method public boolean hasSpeedAccuracy();
method public boolean hasVerticalAccuracy();
- method public boolean isFromMockProvider();
+ method @Deprecated public boolean isFromMockProvider();
+ method public boolean isMock();
method @Deprecated public void removeAccuracy();
method @Deprecated public void removeAltitude();
method @Deprecated public void removeBearing();
@@ -19821,6 +19906,7 @@
method public void setExtras(@Nullable android.os.Bundle);
method public void setLatitude(double);
method public void setLongitude(double);
+ method public void setMock(boolean);
method public void setProvider(String);
method public void setSpeed(float);
method public void setSpeedAccuracyMetersPerSecond(float);
@@ -20099,6 +20185,14 @@
method public android.media.AudioAttributes.Builder setUsage(int);
}
+ public class AudioDescriptor {
+ method @NonNull public byte[] getDescriptor();
+ method public int getEncapsulationType();
+ method public int getStandard();
+ field public static final int STANDARD_EDID = 1; // 0x1
+ field public static final int STANDARD_NONE = 0; // 0x0
+ }
+
public abstract class AudioDeviceCallback {
ctor public AudioDeviceCallback();
method public void onAudioDevicesAdded(android.media.AudioDeviceInfo[]);
@@ -20107,6 +20201,7 @@
public final class AudioDeviceInfo {
method @NonNull public String getAddress();
+ method @NonNull public java.util.List<android.media.AudioDescriptor> getAudioDescriptors();
method @NonNull public java.util.List<android.media.AudioProfile> getAudioProfiles();
method @NonNull public int[] getChannelCounts();
method @NonNull public int[] getChannelIndexMasks();
@@ -20569,8 +20664,11 @@
public class AudioProfile {
method @NonNull public int[] getChannelIndexMasks();
method @NonNull public int[] getChannelMasks();
+ method public int getEncapsulationType();
method public int getFormat();
method @NonNull public int[] getSampleRates();
+ field public static final int AUDIO_ENCAPSULATION_TYPE_IEC61937 = 1; // 0x1
+ field public static final int AUDIO_ENCAPSULATION_TYPE_NONE = 0; // 0x0
}
public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
@@ -20863,6 +20961,7 @@
public class CamcorderProfile {
method public static android.media.CamcorderProfile get(int);
method public static android.media.CamcorderProfile get(int, int);
+ method @Nullable public static android.media.EncoderProfiles getAll(@NonNull String, int);
method public static boolean hasProfile(int);
method public static boolean hasProfile(int, int);
field public static final int QUALITY_1080P = 6; // 0x6
@@ -20943,6 +21042,32 @@
field @NonNull public final java.util.UUID uuid;
}
+ public class EncoderProfiles {
+ method @NonNull public java.util.List<android.media.EncoderProfiles.AudioProfile> getAudioProfiles();
+ method public int getDurationSeconds();
+ method public int getFileFormat();
+ method @NonNull public java.util.List<android.media.EncoderProfiles.VideoProfile> getVideoProfiles();
+ }
+
+ public static class EncoderProfiles.AudioProfile {
+ method public int getBitrate();
+ method public int getChannels();
+ method public int getCodec();
+ method @NonNull public String getMediaType();
+ method public int getProfile();
+ method public int getSampleRate();
+ }
+
+ public static class EncoderProfiles.VideoProfile {
+ method public int getBitrate();
+ method public int getCodec();
+ method public int getFrameRate();
+ method public int getHeight();
+ method @NonNull public String getMediaType();
+ method public int getProfile();
+ method public int getWidth();
+ }
+
public class ExifInterface {
ctor public ExifInterface(@NonNull java.io.File) throws java.io.IOException;
ctor public ExifInterface(@NonNull String) throws java.io.IOException;
@@ -21496,7 +21621,7 @@
method public long getPresentationTimeUs();
}
- public class MediaCodec.ParameterDescriptor {
+ public static class MediaCodec.ParameterDescriptor {
method @NonNull public String getName();
method public int getType();
}
@@ -21608,6 +21733,7 @@
field public static final String FEATURE_LowLatency = "low-latency";
field public static final String FEATURE_MultipleFrames = "multiple-frames";
field public static final String FEATURE_PartialFrame = "partial-frame";
+ field public static final String FEATURE_QpBounds = "qp-bounds";
field public static final String FEATURE_SecurePlayback = "secure-playback";
field public static final String FEATURE_TunneledPlayback = "tunneled-playback";
field public int[] colorFormats;
@@ -21826,6 +21952,7 @@
method public android.util.Range<java.lang.Integer> getQualityRange();
method public boolean isBitrateModeSupported(int);
field public static final int BITRATE_MODE_CBR = 2; // 0x2
+ field public static final int BITRATE_MODE_CBR_FD = 3; // 0x3
field public static final int BITRATE_MODE_CQ = 0; // 0x0
field public static final int BITRATE_MODE_VBR = 1; // 0x1
}
@@ -22868,6 +22995,7 @@
method public void setAudioChannels(int);
method public void setAudioEncoder(int) throws java.lang.IllegalStateException;
method public void setAudioEncodingBitRate(int);
+ method public void setAudioProfile(@NonNull android.media.EncoderProfiles.AudioProfile);
method public void setAudioSamplingRate(int);
method public void setAudioSource(int) throws java.lang.IllegalStateException;
method @Deprecated public void setCamera(android.hardware.Camera);
@@ -22896,6 +23024,7 @@
method public void setVideoEncodingBitRate(int);
method public void setVideoEncodingProfileLevel(int, int);
method public void setVideoFrameRate(int) throws java.lang.IllegalStateException;
+ method public void setVideoProfile(@NonNull android.media.EncoderProfiles.VideoProfile);
method public void setVideoSize(int, int) throws java.lang.IllegalStateException;
method public void setVideoSource(int) throws java.lang.IllegalStateException;
method public void start() throws java.lang.IllegalStateException;
@@ -34916,6 +35045,7 @@
field public static final String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
field public static final String ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS = "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS";
field public static final String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS";
+ field public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS = "com.android.settings.APP_OPEN_BY_DEFAULT_SETTINGS";
field public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
field public static final String ACTION_APP_USAGE_SETTINGS = "android.settings.action.APP_USAGE_SETTINGS";
field public static final String ACTION_AUTO_ROTATE_SETTINGS = "android.settings.AUTO_ROTATE_SETTINGS";
@@ -38220,6 +38350,25 @@
}
+package android.service.dataloader {
+
+ public abstract class DataLoaderService extends android.app.Service {
+ ctor public DataLoaderService();
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader(@NonNull android.content.pm.DataLoaderParams);
+ }
+
+ public static interface DataLoaderService.DataLoader {
+ method public boolean onCreate(@NonNull android.content.pm.DataLoaderParams, @NonNull android.service.dataloader.DataLoaderService.FileSystemConnector);
+ method public boolean onPrepareImage(@NonNull java.util.Collection<android.content.pm.InstallationFile>, @NonNull java.util.Collection<java.lang.String>);
+ }
+
+ public static final class DataLoaderService.FileSystemConnector {
+ method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
+ }
+
+}
+
package android.service.dreams {
public class DreamService extends android.app.Service implements android.view.Window.Callback {
@@ -38969,6 +39118,7 @@
method public void bufferReceived(byte[]) throws android.os.RemoteException;
method public void endOfSpeech() throws android.os.RemoteException;
method public void error(int) throws android.os.RemoteException;
+ method @NonNull public android.content.AttributionSource getCallingAttributionSource();
method public int getCallingUid();
method public void partialResults(android.os.Bundle) throws android.os.RemoteException;
method public void readyForSpeech(android.os.Bundle) throws android.os.RemoteException;
@@ -40162,6 +40312,7 @@
field public static final int DURATION_MEDIUM = 2; // 0x2
field public static final int DURATION_SHORT = 1; // 0x1
field public static final int DURATION_VERY_SHORT = 0; // 0x0
+ field public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L; // 0x95f3323L
field public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
@@ -41768,7 +41919,7 @@
method @Deprecated public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
method @Deprecated public void onCallForwardingIndicatorChanged(boolean);
- method @Deprecated public void onCallStateChanged(int, String);
+ method @Deprecated @RequiresPermission(value=android.Manifest.permission.READ_PHONE_STATE, conditional=true) public void onCallStateChanged(int, String);
method @Deprecated public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
method @Deprecated public void onCellLocationChanged(android.telephony.CellLocation);
method @Deprecated public void onDataActivity(int);
@@ -42314,7 +42465,7 @@
}
public static interface TelephonyCallback.CallStateListener {
- method public void onCallStateChanged(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallStateChanged(int);
}
public static interface TelephonyCallback.CarrierNetworkListener {
@@ -42342,7 +42493,7 @@
}
public static interface TelephonyCallback.DisplayInfoListener {
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+ method public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
}
public static interface TelephonyCallback.EmergencyNumberListListener {
@@ -42404,7 +42555,8 @@
method public int getActiveModemCount();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCallComposerStatus();
- method public int getCallState();
+ method @Deprecated @RequiresPermission(value=android.Manifest.permission.READ_PHONE_STATE, conditional=true) public int getCallState();
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getCallStateForSubscription();
method public int getCardIdForDefaultEuicc();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
method public int getCarrierIdFromSimMccMnc();
@@ -42829,6 +42981,7 @@
field public static final int PROTOCOL_NON_IP = 4; // 0x4
field public static final int PROTOCOL_PPP = 3; // 0x3
field public static final int PROTOCOL_UNSTRUCTURED = 5; // 0x5
+ field public static final int TYPE_BIP = 8192; // 0x2000
field public static final int TYPE_CBS = 128; // 0x80
field public static final int TYPE_DEFAULT = 17; // 0x11
field public static final int TYPE_DUN = 8; // 0x8
@@ -42840,6 +42993,7 @@
field public static final int TYPE_MCX = 1024; // 0x400
field public static final int TYPE_MMS = 2; // 0x2
field public static final int TYPE_SUPL = 4; // 0x4
+ field public static final int TYPE_VSIM = 4096; // 0x1000
field public static final int TYPE_XCAP = 2048; // 0x800
}
@@ -48361,6 +48515,7 @@
method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void dispatchProvideAutofillStructure(@NonNull android.view.ViewStructure, int);
method public void dispatchProvideStructure(android.view.ViewStructure);
+ method public void dispatchRequestTranslation(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @Nullable android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
method public void dispatchScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>);
@@ -48556,6 +48711,7 @@
method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarThumbDrawable();
method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarTrackDrawable();
method public int getVerticalScrollbarWidth();
+ method @Nullable public android.view.translation.ViewTranslationCallback getViewTranslationCallback();
method public android.view.ViewTreeObserver getViewTreeObserver();
method public int getVisibility();
method public final int getWidth();
@@ -48655,6 +48811,8 @@
method protected void onCreateContextMenu(android.view.ContextMenu);
method protected int[] onCreateDrawableState(int);
method public android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.EditorInfo);
+ method @Nullable public android.view.translation.ViewTranslationRequest onCreateTranslationRequest(@NonNull int[]);
+ method public void onCreateTranslationRequests(@NonNull long[], @NonNull int[], @NonNull java.util.function.Consumer<android.view.translation.ViewTranslationRequest>);
method @CallSuper protected void onDetachedFromWindow();
method protected void onDisplayHint(int);
method public boolean onDragEvent(android.view.DragEvent);
@@ -48699,6 +48857,8 @@
method public void onStartTemporaryDetach();
method public boolean onTouchEvent(android.view.MotionEvent);
method public boolean onTrackballEvent(android.view.MotionEvent);
+ method public void onTranslationResponse(@NonNull android.view.translation.ViewTranslationResponse);
+ method public void onTranslationResponse(@NonNull android.util.LongSparseArray<android.view.translation.ViewTranslationResponse>);
method @CallSuper public void onVisibilityAggregated(boolean);
method protected void onVisibilityChanged(@NonNull android.view.View, int);
method public void onWindowFocusChanged(boolean);
@@ -48907,6 +49067,7 @@
method public void setVerticalScrollbarPosition(int);
method public void setVerticalScrollbarThumbDrawable(@Nullable android.graphics.drawable.Drawable);
method public void setVerticalScrollbarTrackDrawable(@Nullable android.graphics.drawable.Drawable);
+ method public void setViewTranslationCallback(@NonNull android.view.translation.ViewTranslationCallback);
method public void setVisibility(int);
method @Deprecated public void setWillNotCacheDrawing(boolean);
method public void setWillNotDraw(boolean);
@@ -51389,6 +51550,7 @@
field public static final int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5; // 0xfffffffb
field public static final int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; // 0xfffffffd
field public static final int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; // 0xfffffffc
+ field public static final int DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS = -6; // 0xfffffffa
field public static final int DISPLAY_HASH_ERROR_UNKNOWN = -1; // 0xffffffff
}
@@ -52619,11 +52781,16 @@
}
public final class TranslationManager {
- method public void addTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
- method @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationContext);
- method @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getTranslationCapabilities(int, int);
- method @Nullable public android.app.PendingIntent getTranslationSettingsActivityIntent();
- method public void removeTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+ method public void addOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+ method @Deprecated public void addTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+ method @Nullable @WorkerThread public android.view.translation.Translator createOnDeviceTranslator(@NonNull android.view.translation.TranslationContext);
+ method @Deprecated @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationContext);
+ method @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getOnDeviceTranslationCapabilities(int, int);
+ method @Nullable public android.app.PendingIntent getOnDeviceTranslationSettingsActivityIntent();
+ method @Deprecated @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getTranslationCapabilities(int, int);
+ method @Deprecated @Nullable public android.app.PendingIntent getTranslationSettingsActivityIntent();
+ method public void removeOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+ method @Deprecated public void removeTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
}
public final class TranslationRequest implements android.os.Parcelable {
@@ -52729,6 +52896,12 @@
method public void onStarted(@NonNull String, @NonNull String);
}
+ @UiThread public interface ViewTranslationCallback {
+ method public boolean onClearTranslation(@NonNull android.view.View);
+ method public boolean onHideTranslation(@NonNull android.view.View);
+ method public boolean onShowTranslation(@NonNull android.view.View);
+ }
+
public final class ViewTranslationRequest implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.view.autofill.AutofillId getAutofillId();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 5925b72..4fb9926 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -196,7 +196,6 @@
method @NonNull public static String blockedReasonsToString(int);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int);
- method public static boolean isUidBlocked(int, boolean);
method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean);
method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int);
method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
@@ -301,7 +300,8 @@
public class StorageManager {
method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int);
method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int);
- field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 0; // 0x0
+ field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1; // 0x1
+ field public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0; // 0x0
}
public final class StorageVolume implements android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 55816df..1b82c03 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3,6 +3,7 @@
public static final class Manifest.permission {
field public static final String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS";
+ field public static final String ACCESS_BLOBS_ACROSS_USERS = "android.permission.ACCESS_BLOBS_ACROSS_USERS";
field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB";
@@ -179,6 +180,7 @@
field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String NETWORK_STATS_PROVIDER = "android.permission.NETWORK_STATS_PROVIDER";
+ field public static final String NFC_SET_CONTROLLER_ALWAYS_ON = "android.permission.NFC_SET_CONTROLLER_ALWAYS_ON";
field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
@@ -186,6 +188,7 @@
field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY";
field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS";
+ field public static final String OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD = "android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD";
field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD";
@@ -243,6 +246,7 @@
field public static final String REVIEW_ACCESSIBILITY_SERVICES = "android.permission.REVIEW_ACCESSIBILITY_SERVICES";
field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
field public static final String ROTATE_SURFACE_FLINGER = "android.permission.ROTATE_SURFACE_FLINGER";
+ field public static final String SCHEDULE_PRIORITIZED_ALARM = "android.permission.SCHEDULE_PRIORITIZED_ALARM";
field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
field public static final String SECURE_ELEMENT_PRIVILEGED_OPERATION = "android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION";
field public static final String SEND_CATEGORY_CAR_NOTIFICATIONS = "android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS";
@@ -317,6 +321,7 @@
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
field public static final int sdkVersion = 16844304; // 0x1010610
field public static final int supportsAmbientMode = 16844173; // 0x101058d
+ field public static final int throttleDurationMillis = 16844360; // 0x1010648
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -422,6 +427,7 @@
public class AlarmManager {
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.PendingIntent, android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.SCHEDULE_PRIORITIZED_ALARM) public void setPrioritized(int, long, long, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.app.AlarmManager.OnAlarmListener);
}
public class AppOpsManager {
@@ -592,7 +598,7 @@
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 @Nullable public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOps(@Nullable 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);
@@ -951,6 +957,9 @@
field @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3
field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
+ field public static final String REQUIRED_APP_MANAGED_DEVICE = "android.app.REQUIRED_APP_MANAGED_DEVICE";
+ field public static final String REQUIRED_APP_MANAGED_PROFILE = "android.app.REQUIRED_APP_MANAGED_PROFILE";
+ field public static final String REQUIRED_APP_MANAGED_USER = "android.app.REQUIRED_APP_MANAGED_USER";
field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
@@ -1223,6 +1232,21 @@
method public static boolean isChangeEnabled(long);
method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int);
+ method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void setPackageOverride(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>);
+ }
+
+ public final class PackageOverride {
+ method public long getMaxVersionCode();
+ method public long getMinVersionCode();
+ method public boolean isEnabled();
+ }
+
+ public static final class PackageOverride.Builder {
+ ctor public PackageOverride.Builder();
+ method @NonNull public android.app.compat.PackageOverride build();
+ method @NonNull public android.app.compat.PackageOverride.Builder setEnabled(boolean);
+ method @NonNull public android.app.compat.PackageOverride.Builder setMaxVersionCode(long);
+ method @NonNull public android.app.compat.PackageOverride.Builder setMinVersionCode(long);
}
}
@@ -1711,10 +1735,20 @@
field public static final int CAPABILITY_POSSESSED = 40; // 0x28
}
+ public final class ExternalTimeSuggestion implements android.os.Parcelable {
+ ctor public ExternalTimeSuggestion(long, long);
+ method public void addDebugInfo(@NonNull java.lang.String...);
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getDebugInfo();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.time.ExternalTimeSuggestion> CREATOR;
+ }
+
public final class TimeManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void addTimeZoneDetectorListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.time.TimeManager.TimeZoneDetectorListener);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public android.app.time.TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig();
method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void removeTimeZoneDetectorListener(@NonNull android.app.time.TimeManager.TimeZoneDetectorListener);
+ method @RequiresPermission(android.Manifest.permission.SUGGEST_EXTERNAL_TIME) public void suggestExternalTime(@NonNull android.app.time.ExternalTimeSuggestion);
method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public boolean updateTimeZoneConfiguration(@NonNull android.app.time.TimeZoneConfiguration);
}
@@ -1953,7 +1987,6 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
- method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
}
public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
@@ -2180,6 +2213,7 @@
package android.companion {
public final class CompanionDeviceManager {
+ method @RequiresPermission("android.permission.ASSOCIATE_COMPANION_DEVICES") public boolean associate(@NonNull String, @NonNull android.net.MacAddress);
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, int);
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
}
@@ -2195,6 +2229,14 @@
method @NonNull public java.io.File getDeviceProtectedDataDirForUser(@NonNull android.os.UserHandle);
}
+ public final class AttributionSource implements android.os.Parcelable {
+ method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public java.util.Set<java.lang.String> getRenouncedPermissions();
+ }
+
+ public static final class AttributionSource.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.AttributionSource.Builder setRenouncedPermissions(@NonNull java.util.Set<java.lang.String>);
+ }
+
public abstract class BroadcastReceiver {
method @NonNull public final android.os.UserHandle getSendingUser();
}
@@ -2269,11 +2311,11 @@
}
public final class ContextParams {
- method @Nullable @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public java.util.Set<java.lang.String> getRenouncedPermissions();
+ method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public java.util.Set<java.lang.String> getRenouncedPermissions();
}
public static final class ContextParams.Builder {
- method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.ContextParams.Builder setRenouncedPermissions(@Nullable java.util.Set<java.lang.String>);
+ method @NonNull @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) public android.content.ContextParams.Builder setRenouncedPermissions(@NonNull java.util.Set<java.lang.String>);
}
public class ContextWrapper extends android.content.Context {
@@ -2465,14 +2507,6 @@
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES) public void startActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
}
- public final class InstallationFile {
- method public long getLengthBytes();
- method public int getLocation();
- method @Nullable public byte[] getMetadata();
- method @NonNull public String getName();
- method @Nullable public byte[] getSignature();
- }
-
public final class InstantAppInfo implements android.os.Parcelable {
ctor public InstantAppInfo(android.content.pm.ApplicationInfo, String[], String[]);
ctor public InstantAppInfo(String, CharSequence, String[], String[]);
@@ -2567,7 +2601,6 @@
public static class PackageInstaller.Session implements java.io.Closeable {
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender);
- method public void removeFile(int, @NonNull String);
}
public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
@@ -2811,7 +2844,9 @@
method @NonNull public android.content.pm.SuspendDialogInfo.Builder setMessage(@StringRes int);
method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonAction(int);
method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(@StringRes int);
+ method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(@NonNull String);
method @NonNull public android.content.pm.SuspendDialogInfo.Builder setTitle(@StringRes int);
+ method @NonNull public android.content.pm.SuspendDialogInfo.Builder setTitle(@NonNull String);
}
}
@@ -2877,7 +2912,7 @@
}
public final class DomainVerificationManager {
- method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Nullable @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List<android.content.pm.verify.domain.DomainOwner> getOwnersForDomain(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> queryValidVerificationPackageNames();
method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -4831,7 +4866,7 @@
public class Location implements android.os.Parcelable {
method public boolean isComplete();
method public void makeComplete();
- method public void setIsFromMockProvider(boolean);
+ method @Deprecated public void setIsFromMockProvider(boolean);
field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
}
@@ -5239,7 +5274,6 @@
method @Nullable public String getClientPackageName();
method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String);
method @Nullable public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String);
- method public void registerRouteCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaRouter2.RouteCallback);
method public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
method public void startScan();
method public void stopScan();
@@ -5263,6 +5297,10 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void ensureDefaultRingtones(@NonNull android.content.Context);
}
+ public final class RouteDiscoveryPreference implements android.os.Parcelable {
+ field public static final android.media.RouteDiscoveryPreference EMPTY;
+ }
+
}
package android.media.audiofx {
@@ -7874,10 +7912,10 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAlwaysOnEnabled();
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAlwaysOnSupported();
+ method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
+ method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setAlwaysOn(boolean);
+ method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
}
@@ -8290,12 +8328,8 @@
}
public class PowerExemptionManager {
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToPermanentAllowList(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToPermanentAllowList(@NonNull java.util.List<java.lang.String>);
method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void addToTemporaryAllowList(@NonNull String, int, @Nullable String, long);
method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long addToTemporaryAllowListForEvent(@NonNull String, int, @Nullable String, int);
- method @NonNull @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public int[] getAllowListedAppIds(boolean);
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromPermanentAllowList(@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
@@ -8308,6 +8342,7 @@
field public static final int REASON_UNKNOWN = 0; // 0x0
field public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
field public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
+ field public static final int TEMPORARY_ALLOW_LIST_TYPE_NONE = -1; // 0xffffffff
}
public final class PowerManager {
@@ -8967,6 +9002,7 @@
field public static final String NAMESPACE_GAME_DRIVER = "game_driver";
field public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
field public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
+ field public static final String NAMESPACE_MEDIA = "media";
field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
field public static final String NAMESPACE_OTA = "ota";
@@ -9737,24 +9773,6 @@
}
-package android.service.dataloader {
-
- public abstract class DataLoaderService extends android.app.Service {
- ctor public DataLoaderService();
- method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader(@NonNull android.content.pm.DataLoaderParams);
- }
-
- public static interface DataLoaderService.DataLoader {
- method public boolean onCreate(@NonNull android.content.pm.DataLoaderParams, @NonNull android.service.dataloader.DataLoaderService.FileSystemConnector);
- method public boolean onPrepareImage(@NonNull java.util.Collection<android.content.pm.InstallationFile>, @NonNull java.util.Collection<java.lang.String>);
- }
-
- public static final class DataLoaderService.FileSystemConnector {
- method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
- }
-
-}
-
package android.service.displayhash {
public final class DisplayHashParams implements android.os.Parcelable {
@@ -9781,6 +9799,7 @@
method @NonNull public abstract java.util.Map<java.lang.String,android.service.displayhash.DisplayHashParams> onGetDisplayHashAlgorithms();
method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash);
field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHasherService";
+ field public static final String SERVICE_META_DATA = "android.displayhash.display_hasher_service";
}
}
@@ -10345,7 +10364,7 @@
package android.service.voice {
- public class AlwaysOnHotwordDetector {
+ public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector {
method @Nullable public android.content.Intent createEnrollIntent();
method @Nullable public android.content.Intent createReEnrollIntent();
method @Nullable public android.content.Intent createUnEnrollIntent();
@@ -10355,7 +10374,10 @@
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int);
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
+ method @Nullable public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition();
+ method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
@@ -10373,18 +10395,17 @@
field @Deprecated public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
}
- public abstract static class AlwaysOnHotwordDetector.Callback {
+ public abstract static class AlwaysOnHotwordDetector.Callback implements android.service.voice.HotwordDetector.Callback {
ctor public AlwaysOnHotwordDetector.Callback();
method public abstract void onAvailabilityChanged(int);
- method public abstract void onDetected(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload);
- method public abstract void onError();
- method public abstract void onRecognitionPaused();
- method public abstract void onRecognitionResumed();
+ method public void onHotwordDetectionServiceInitialized(int);
method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
}
public static class AlwaysOnHotwordDetector.EventPayload {
+ method @Nullable public android.os.ParcelFileDescriptor getAudioStream();
method @Nullable public android.media.AudioFormat getCaptureAudioFormat();
+ method @Nullable public android.service.voice.HotwordDetectedResult getHotwordDetectedResult();
method @Nullable public byte[] getTriggerAudio();
}
@@ -10422,23 +10443,41 @@
public abstract class HotwordDetectionService extends android.app.Service {
ctor public HotwordDetectionService();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method public void onDetectFromDspSource(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.DspHotwordDetectionCallback);
- method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
+ method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.Callback);
+ method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.service.voice.HotwordDetectionService.Callback);
+ method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle, @NonNull android.service.voice.HotwordDetectionService.Callback);
+ method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer);
+ field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1; // 0x1
+ field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_2 = 2; // 0x2
+ field public static final int INITIALIZATION_STATUS_SUCCESS = 0; // 0x0
+ field public static final int INITIALIZATION_STATUS_UNKNOWN = 100; // 0x64
field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService";
}
- public static final class HotwordDetectionService.DspHotwordDetectionCallback {
- method public void onDetected();
+ public static final class HotwordDetectionService.Callback {
+ method public void onDetected(@Nullable android.service.voice.HotwordDetectedResult);
method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
}
public interface HotwordDetector {
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
+ method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
+ method public boolean stopRecognition();
field public static final int CONFIDENCE_LEVEL_HIGH = 3; // 0x3
field public static final int CONFIDENCE_LEVEL_LOW = 1; // 0x1
field public static final int CONFIDENCE_LEVEL_MEDIUM = 2; // 0x2
field public static final int CONFIDENCE_LEVEL_NONE = 0; // 0x0
}
+ public static interface HotwordDetector.Callback {
+ method public void onDetected(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload);
+ method public void onError();
+ method public void onHotwordDetectionServiceInitialized(int);
+ method public void onRecognitionPaused();
+ method public void onRecognitionResumed();
+ method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
+ }
+
public final class HotwordRejectedResult implements android.os.Parcelable {
method public int describeContents();
method public int getConfidenceLevel();
@@ -10449,6 +10488,7 @@
public class VoiceInteractionService extends android.app.Service {
method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, android.service.voice.AlwaysOnHotwordDetector.Callback);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.service.voice.HotwordDetector.Callback);
method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
}
@@ -10853,7 +10893,7 @@
method public java.util.List<android.telecom.PhoneAccount> getAllPhoneAccounts();
method public int getAllPhoneAccountsCount();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean);
- method public int getCallState();
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}, conditional=true) public int getCallState();
method public android.telecom.PhoneAccountHandle getConnectionManager();
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);
@@ -12139,6 +12179,7 @@
method public static int getApnTypeInt(@NonNull String);
method @NonNull public static String getApnTypeString(int);
field public static final String TYPE_ALL_STRING = "*";
+ field public static final String TYPE_BIP_STRING = "bip";
field public static final String TYPE_CBS_STRING = "cbs";
field public static final String TYPE_DEFAULT_STRING = "default";
field public static final String TYPE_DUN_STRING = "dun";
@@ -12150,6 +12191,7 @@
field public static final String TYPE_MCX_STRING = "mcx";
field public static final String TYPE_MMS_STRING = "mms";
field public static final String TYPE_SUPL_STRING = "supl";
+ field public static final String TYPE_VSIM_STRING = "vsim";
field public static final String TYPE_XCAP_STRING = "xcap";
}
@@ -12299,11 +12341,11 @@
public final class EpsBearerQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
method public int describeContents();
- method public long getGuaranteedDownlinkBitRate();
- method public long getGuaranteedUplinkBitRate();
- method public long getMaxDownlinkBitRate();
- method public long getMaxUplinkBitRate();
- method public int getQci();
+ method public long getGuaranteedDownlinkBitRateKbps();
+ method public long getGuaranteedUplinkBitRateKbps();
+ method public long getMaxDownlinkBitRateKbps();
+ method public long getMaxUplinkBitRateKbps();
+ method public int getQosIdentifier();
method @NonNull public java.util.List<java.net.InetSocketAddress> getRemoteAddresses();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.EpsBearerQosSessionAttributes> CREATOR;
@@ -12311,13 +12353,13 @@
public final class NrQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
method public int describeContents();
- method public int get5Qi();
- method public long getAveragingWindow();
- method public long getGuaranteedDownlinkBitRate();
- method public long getGuaranteedUplinkBitRate();
- method public long getMaxDownlinkBitRate();
- method public long getMaxUplinkBitRate();
- method public int getQfi();
+ method @NonNull public java.time.Duration getBitRateWindowDuration();
+ method public long getGuaranteedDownlinkBitRateKbps();
+ method public long getGuaranteedUplinkBitRateKbps();
+ method public long getMaxDownlinkBitRateKbps();
+ method public long getMaxUplinkBitRateKbps();
+ method @IntRange(from=1, to=63) public int getQosFlowIdentifier();
+ method public int getQosIdentifier();
method @NonNull public java.util.List<java.net.InetSocketAddress> getRemoteAddresses();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NrQosSessionAttributes> CREATOR;
@@ -13633,13 +13675,13 @@
ctor @Deprecated public RcsFeature();
ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
- method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
+ method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
+ method public void destroyCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
method public void onFeatureReady();
method public void onFeatureRemoved();
method public boolean queryCapabilityConfiguration(int, int);
method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
- method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
}
public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
@@ -13852,7 +13894,7 @@
}
public class RcsCapabilityExchangeImplBase {
- ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
+ ctor public RcsCapabilityExchangeImplBase();
method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback);
method public void subscribeForCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
@@ -14070,7 +14112,7 @@
package android.uwb {
public final class AngleMeasurement implements android.os.Parcelable {
- ctor public AngleMeasurement(double, double, double);
+ ctor public AngleMeasurement(@FloatRange(from=-3.141592653589793, to=3.141592653589793) double, @FloatRange(from=0.0, to=3.141592653589793) double, @FloatRange(from=0.0, to=1.0) double);
method public int describeContents();
method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
method @FloatRange(from=0.0, to=3.141592653589793) public double getErrorRadians();
@@ -14709,6 +14751,7 @@
method public default boolean onCheckIsTextEditor();
method public void onConfigurationChanged(android.content.res.Configuration);
method public android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.EditorInfo);
+ method @Nullable public default void onCreateTranslationRequests(@NonNull long[], @NonNull int[], @NonNull java.util.function.Consumer<android.view.translation.ViewTranslationRequest>);
method public void onDetachedFromWindow();
method public boolean onDragEvent(android.view.DragEvent);
method public void onDraw(android.graphics.Canvas);
@@ -14733,6 +14776,7 @@
method public void onStartTemporaryDetach();
method public boolean onTouchEvent(android.view.MotionEvent);
method public boolean onTrackballEvent(android.view.MotionEvent);
+ method public default void onTranslationResponse(@NonNull android.util.LongSparseArray<android.view.translation.ViewTranslationResponse>);
method public void onVisibilityChanged(android.view.View, int);
method public void onWindowFocusChanged(boolean);
method public void onWindowVisibilityChanged(int);
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 3926e39..b50b8dd 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -48,6 +48,14 @@
}
+package android.bluetooth {
+
+ public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
+ }
+
+}
+
package android.content {
public class Intent implements java.lang.Cloneable android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 97ad48c..56b0a9d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -220,6 +220,7 @@
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void rebootHistory(long);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void reloadNonHistoricalState();
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void resetHistoryParameters();
+ method @RequiresPermission("android.permission.MANAGE_APPOPS") public void resetPackageOpsNoHistory(@NonNull String);
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 public static int strOpToOp(@NonNull String);
@@ -252,6 +253,14 @@
method public void offsetBeginAndEndTime(long);
}
+ public class BroadcastOptions {
+ ctor public BroadcastOptions(@NonNull android.os.Bundle);
+ method public long getTemporaryAppAllowlistDuration();
+ method @Nullable public String getTemporaryAppAllowlistReason();
+ method public int getTemporaryAppAllowlistReasonCode();
+ method public int getTemporaryAppAllowlistType();
+ }
+
public class DownloadManager {
field public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri";
}
@@ -272,6 +281,10 @@
method public abstract void onHomeVisibilityChanged(boolean);
}
+ public class Notification implements android.os.Parcelable {
+ method public boolean shouldShowForegroundImmediately();
+ }
+
public final class NotificationChannel implements android.os.Parcelable {
method public int getOriginalImportance();
method public boolean isBlockable();
@@ -650,6 +663,11 @@
package android.content {
+ public final class AttributionSource implements android.os.Parcelable {
+ ctor public AttributionSource(int, @Nullable String, @Nullable String);
+ ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable android.content.AttributionSource);
+ }
+
public final class AutofillOptions implements android.os.Parcelable {
ctor public AutofillOptions(int, boolean);
method public int describeContents();
@@ -1136,10 +1154,13 @@
}
public final class DisplayManager {
- method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public int getRefreshRateSwitchingType();
+ method public boolean areUserDisabledHdrTypesAllowed();
+ method @NonNull public int[] getUserDisabledHdrTypes();
method public boolean isMinimalPostProcessingRequested(int);
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
field public static final int SWITCHING_TYPE_NONE = 0; // 0x0
@@ -1355,9 +1376,12 @@
}
public class AudioManager {
+ method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
+ method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
method public boolean hasRegisteredDynamicPolicy();
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice();
+ method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int requestAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String, int, int);
}
public static final class AudioRecord.MetricsConstants {
@@ -1632,6 +1656,7 @@
field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8
field public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; // 0x182b7
field public static final int LAST_ISOLATED_UID = 99999; // 0x1869f
+ field public static final int NFC_UID = 1027; // 0x403
field public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; // 0x64
}
@@ -2021,6 +2046,7 @@
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 ARE_USER_DISABLED_HDR_FORMATS_ALLOWED = "are_user_disabled_hdr_formats_allowed";
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 DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW = "enable_non_resizable_multi_window";
@@ -2035,6 +2061,7 @@
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 USER_DISABLED_HDR_FORMATS = "user_disabled_hdr_formats";
field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
}
@@ -2585,6 +2612,7 @@
}
public final class Display {
+ method @NonNull public int[] getReportedHdrTypes();
method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
method public int getType();
method public boolean hasAccess(int);
@@ -2635,7 +2663,9 @@
public final class SurfaceControl implements android.os.Parcelable {
ctor public SurfaceControl(@NonNull android.view.SurfaceControl, @NonNull String);
method public static long acquireFrameRateFlexibilityToken();
+ method @NonNull public static android.os.IBinder getInternalDisplayToken();
method public boolean isSameSurface(@NonNull android.view.SurfaceControl);
+ method public static void overrideHdrTypes(@NonNull android.os.IBinder, @NonNull int[]);
method public static void releaseFrameRateFlexibilityToken(long);
}
@@ -2847,6 +2877,14 @@
}
+package android.view.displayhash {
+
+ public final class DisplayHashManager {
+ method @RequiresPermission("android.permission.READ_FRAME_BUFFER") public void setDisplayHashThrottlingEnabled(boolean);
+ }
+
+}
+
package android.view.inputmethod {
public final class InlineSuggestion implements android.os.Parcelable {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 86e2723..0f38b5f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -642,7 +642,7 @@
* protected void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
*
- * SharedPreferences mPrefs = getSharedPreferences();
+ * mPrefs = getSharedPreferences(getLocalClassName(), MODE_PRIVATE);
* mCurViewMode = mPrefs.getInt("view_mode", DAY_VIEW_MODE);
* }
*
@@ -5246,13 +5246,40 @@
if (requestCode < 0) {
throw new IllegalArgumentException("requestCode should be >= 0");
}
+
if (mHasCurrentPermissionsRequest) {
Log.w(TAG, "Can request only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
}
- Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
+
+ List<String> filteredPermissions = null;
+
+ if (!getAttributionSource().getRenouncedPermissions().isEmpty()) {
+ final int permissionCount = permissions.length;
+ for (int i = 0; i < permissionCount; i++) {
+ if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) {
+ if (filteredPermissions == null) {
+ filteredPermissions = new ArrayList<>(i);
+ for (int j = 0; j < i; j++) {
+ filteredPermissions.add(permissions[i]);
+ }
+ }
+ } else if (filteredPermissions != null) {
+ filteredPermissions.add(permissions[i]);
+ }
+ }
+ }
+
+ final Intent intent;
+ if (filteredPermissions == null) {
+ intent = getPackageManager().buildRequestPermissionsIntent(permissions);
+ } else {
+ intent = getPackageManager().buildRequestPermissionsIntent(
+ filteredPermissions.toArray(new String[0]));
+ }
+
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f7a3514..a7d5b05 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -30,8 +30,8 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.PowerWhitelistManager.ReasonCode;
-import android.os.PowerWhitelistManager.TempAllowListType;
+import android.os.PowerExemptionManager.ReasonCode;
+import android.os.PowerExemptionManager.TempAllowListType;
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
import android.util.ArraySet;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 7e4af1a..f76e1c0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -50,6 +50,7 @@
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
+import android.content.AttributionSource;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -5955,7 +5956,7 @@
*
* @return The historical ops for the attribution.
*/
- public @Nullable AttributedHistoricalOps getAttributedOps(@NonNull String attributionTag) {
+ public @Nullable AttributedHistoricalOps getAttributedOps(@Nullable String attributionTag) {
if (mAttributedHistoricalOps == null) {
return null;
}
@@ -6480,7 +6481,7 @@
* Gets number of discrete historical app ops.
*
* @return The number historical app ops.
- * @see #getOpAt(int)
+ * @see #getDiscreteAccessAt(int)
*/
public @IntRange(from = 0) int getDiscreteAccessCount() {
if (mDiscreteAccesses == null) {
@@ -6494,7 +6495,7 @@
*
* @param index The index to lookup.
* @return The op at the given index.
- * @see #getOpCount()
+ * @see #getDiscreteAccessCount()
*/
public @NonNull AttributedOpEntry getDiscreteAccessAt(@IntRange(from = 0) int index) {
if (mDiscreteAccesses == null) {
@@ -7979,7 +7980,7 @@
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
- boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID ? true : false;
+ boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
@@ -8033,14 +8034,9 @@
*/
public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid,
@Nullable String proxiedAttributionTag, @Nullable String message) {
- int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, proxiedAttributionTag,
- message);
- if (mode == MODE_ERRORED) {
- throw new SecurityException("Proxy package " + mContext.getOpPackageName()
- + " from uid " + Process.myUid() + " or calling package " + proxiedPackageName
- + " from uid " + proxiedUid + " not allowed to perform " + sOpNames[op]);
- }
- return mode;
+ return noteProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
+ new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag)),
+ message, /*skipProxyOperation*/ false);
}
/**
@@ -8069,6 +8065,36 @@
}
/**
+ * Make note of an application performing an operation on behalf of another application(s).
+ *
+ * @param op The operation to note. One of the OPSTR_* constants.
+ * @param attributionSource The permission identity for which to note.
+ * @param message A message describing the reason the op was noted
+ * @param skipProxyOperation Whether to skip the proxy note.
+ *
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
+ * if it is not allowed and should be silently ignored (without causing the app to crash).
+ *
+ * @throws SecurityException If the any proxying operations in the permission identityf
+ * chain fails.
+ *
+ * @hide
+ */
+ public int noteProxyOp(@NonNull int op, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean skipProxyOperation) {
+ final int mode = noteProxyOpNoThrow(op, attributionSource, message, skipProxyOperation);
+ if (mode == MODE_ERRORED) {
+ throw new SecurityException("Proxy package "
+ + attributionSource.getPackageName() + " from uid "
+ + attributionSource.getUid() + " or calling package "
+ + attributionSource.getNextPackageName() + " from uid "
+ + attributionSource.getNextUid() + " not allowed to perform "
+ + sOpNames[op]);
+ }
+ return mode;
+ }
+
+ /**
* @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String, String)} instead
*/
@Deprecated
@@ -8093,24 +8119,36 @@
*/
public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
int proxiedUid, @Nullable String proxiedAttributionTag, @Nullable String message) {
- return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid,
- proxiedAttributionTag, message);
+ return noteProxyOpNoThrow(strOpToOp(op), new AttributionSource(
+ mContext.getAttributionSource(), new AttributionSource(proxiedUid,
+ proxiedPackageName, proxiedAttributionTag)), message,
+ /*skipProxyOperation*/ false);
}
/**
- * @see #noteProxyOpNoThrow(String, String, int, String, String)
+ * Make note of an application performing an operation on behalf of another application(s).
+ *
+ * @param op The operation to note. One of the OPSTR_* constants.
+ * @param attributionSource The permission identity for which to note.
+ * @param message A message describing the reason the op was noted
+ * @param skipProxyOperation Whether to note op for the proxy
+ *
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
+ * if it is not allowed and should be silently ignored (without causing the app to crash).
*
* @hide
*/
@SuppressWarnings("AndroidFrameworkClientSidePermissionCheck")
- public int noteProxyOpNoThrow(int op, @Nullable String proxiedPackageName, int proxiedUid,
- @Nullable String proxiedAttributionTag, @Nullable String message) {
+ public int noteProxyOpNoThrow(int op, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean skipProxyOperation) {
int myUid = Process.myUid();
try {
collectNoteOpCallsForValidation(op);
- int collectionMode = getNotedOpCollectionMode(proxiedUid, proxiedPackageName, op);
- boolean shouldCollectMessage = myUid == Process.SYSTEM_UID ? true : false;
+ int collectionMode = getNotedOpCollectionMode(
+ attributionSource.getNextUid(),
+ attributionSource.getNextAttributionTag(), op);
+ boolean shouldCollectMessage = (myUid == Process.SYSTEM_UID);
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
@@ -8119,20 +8157,19 @@
}
}
- int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
- proxiedAttributionTag, myUid, mContext.getOpPackageName(),
- mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message,
- shouldCollectMessage);
+ int mode = mService.noteProxyOperation(op, attributionSource,
+ collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage, skipProxyOperation);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
- collectNotedOpForSelf(op, proxiedAttributionTag);
+ collectNotedOpForSelf(op, attributionSource.getNextAttributionTag());
} else if (collectionMode == COLLECT_SYNC
// Only collect app-ops when the proxy is trusted
&& (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
myUid) == PackageManager.PERMISSION_GRANTED ||
- Binder.getCallingUid() == proxiedUid)) {
- collectNotedOpSync(op, proxiedAttributionTag);
+ Binder.getCallingUid() == attributionSource.getNextUid())) {
+ collectNotedOpSync(op, attributionSource.getNextAttributionTag());
}
}
@@ -8424,7 +8461,7 @@
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
- boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID ? true : false;
+ boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
@@ -8450,6 +8487,7 @@
throw e.rethrowFromSystemServer();
}
}
+
/**
* Report that an application has started executing a long-running operation on behalf of
* another application when handling an IPC. This function will verify that the calling uid and
@@ -8470,19 +8508,45 @@
*/
public int startProxyOp(@NonNull String op, int proxiedUid, @NonNull String proxiedPackageName,
@Nullable String proxiedAttributionTag, @Nullable String message) {
- final int mode = startProxyOpNoThrow(op, proxiedUid, proxiedPackageName,
- proxiedAttributionTag, message);
+ return startProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
+ new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag)),
+ message, /*skipProxyOperation*/ false);
+ }
+
+ /**
+ * Report that an application has started executing a long-running operation on behalf of
+ * another application for the attribution chain specified by the {@link AttributionSource}}.
+ *
+ * @param op The op to note
+ * @param attributionSource The permission identity for which to check
+ * @param message A message describing the reason the op was noted
+ * @param skipProxyOperation Whether to skip the proxy start.
+ *
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
+ * if it is not allowed and should be silently ignored (without causing the app to crash).
+ *
+ * @throws SecurityException If the any proxying operations in the permission identity
+ * chain fails.
+ *
+ * @hide
+ */
+ public int startProxyOp(@NonNull String op, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean skipProxyOperation) {
+ final int mode = startProxyOpNoThrow(AppOpsManager.strOpToOp(op), attributionSource,
+ message, skipProxyOperation);
if (mode == MODE_ERRORED) {
- throw new SecurityException("Proxy package " + mContext.getOpPackageName()
- + " from uid " + Process.myUid() + " or calling package " + proxiedPackageName
- + " from uid " + proxiedUid + " not allowed to perform "
- + sOpNames[strOpToOp(op)]);
+ throw new SecurityException("Proxy package "
+ + attributionSource.getPackageName() + " from uid "
+ + attributionSource.getUid() + " or calling package "
+ + attributionSource.getNextPackageName() + " from uid "
+ + attributionSource.getNextUid() + " not allowed to perform "
+ + op);
}
return mode;
}
/**
- *Like {@link #startProxyOp(String, int, String, String, String)} but instead
+ * Like {@link #startProxyOp(String, int, String, String, String)} but instead
* of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
*
* @see #startProxyOp(String, int, String, String, String)
@@ -8490,11 +8554,28 @@
public int startProxyOpNoThrow(@NonNull String op, int proxiedUid,
@NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag,
@Nullable String message) {
- try {
- int opInt = strOpToOp(op);
+ return startProxyOpNoThrow(AppOpsManager.strOpToOp(op), new AttributionSource(
+ mContext.getAttributionSource(), new AttributionSource(proxiedUid,
+ proxiedPackageName, proxiedAttributionTag)), message,
+ /*skipProxyOperation*/ false);
+ }
- collectNoteOpCallsForValidation(opInt);
- int collectionMode = getNotedOpCollectionMode(proxiedUid, proxiedPackageName, opInt);
+ /**
+ * Like {@link #startProxyOp(String, AttributionSource, String)} but instead
+ * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED} and
+ * the checks is for the attribution chain specified by the {@link AttributionSource}.
+ *
+ * @see #startProxyOp(String, AttributionSource, String)
+ *
+ * @hide
+ */
+ public int startProxyOpNoThrow(int op, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean skipProxyOperation) {
+ try {
+ collectNoteOpCallsForValidation(op);
+ int collectionMode = getNotedOpCollectionMode(
+ attributionSource.getNextUid(),
+ attributionSource.getNextPackageName(), op);
boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID;
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
@@ -8504,24 +8585,23 @@
}
}
- int mode = mService.startProxyOperation(getClientId(), opInt, proxiedUid,
- proxiedPackageName, proxiedAttributionTag, Process.myUid(),
- mContext.getOpPackageName(), mContext.getAttributionTag(), false,
- collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
+ int mode = mService.startProxyOperation(getClientId(), op,
+ attributionSource, false, collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage, skipProxyOperation);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
- collectNotedOpForSelf(opInt, proxiedAttributionTag);
+ collectNotedOpForSelf(op,
+ attributionSource.getNextAttributionTag());
} else if (collectionMode == COLLECT_SYNC
// Only collect app-ops when the proxy is trusted
&& (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
Process.myUid()) == PackageManager.PERMISSION_GRANTED
- || Binder.getCallingUid() == proxiedUid)) {
- collectNotedOpSync(opInt, proxiedAttributionTag);
+ || Binder.getCallingUid() == attributionSource.getNextUid())) {
+ collectNotedOpSync(op, attributionSource.getNextAttributionTag());
}
}
-
return mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8580,22 +8660,37 @@
}
/**
- * Report that an application is no longer performing an operation that had previously
+ * Report that an application is no longer performing an operation that had previously
* been started with {@link #startProxyOp(String, int, String, String, String)}. There is no
* validation of input or result; the parameters supplied here must be the exact same ones
* previously passed in when starting the operation.
+ *
* @param op The operation which was started
- * @param proxiedUid The uid the op was started on behalf of
- * @param proxiedPackageName The package the op was started on behalf of
- * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
- * attribution tag} or {@code null} for default attribution
+ * @param proxiedUid The proxied appp's UID
+ * @param proxiedPackageName The proxied appp's package name
+ * @param proxiedAttributionTag The proxied appp's attribution tag or
+ * {@code null} for default attribution
*/
public void finishProxyOp(@NonNull String op, int proxiedUid,
@NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag) {
+ finishProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
+ new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag)));
+ }
+
+ /**
+ * Report that an application is no longer performing an operation that had previously
+ * been started with {@link #startProxyOp(String, AttributionSource, String)}. There is no
+ * validation of input or result; the parameters supplied here must be the exact same ones
+ * previously passed in when starting the operation.
+ *
+ * @param op The operation which was started
+ * @param attributionSource The permission identity for which to finish
+ *
+ * @hide
+ */
+ public void finishProxyOp(@NonNull String op, @NonNull AttributionSource attributionSource) {
try {
- mService.finishProxyOperation(getClientId(), strOpToOp(op), proxiedUid,
- proxiedPackageName, proxiedAttributionTag, Process.myUid(),
- mContext.getOpPackageName(), mContext.getAttributionTag());
+ mService.finishProxyOperation(getClientId(), strOpToOp(op), attributionSource);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8616,6 +8711,46 @@
}
/**
+ * Get whether you are currently proxying to another package. That applies only
+ * for long running operations like {@link #OP_RECORD_AUDIO}.
+ *
+ * @param op The op.
+ * @param proxyAttributionTag Your attribution tag to query for.
+ * @param proxiedUid The proxied UID to query for.
+ * @param proxiedPackageName The proxied package to query for.
+ * @return Whether you are currently proxying to this target.
+ *
+ * @hide
+ */
+ public boolean isProxying(int op, @NonNull String proxyAttributionTag, int proxiedUid,
+ @NonNull String proxiedPackageName) {
+ try {
+ return mService.isProxying(op, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), proxiedUid, proxiedPackageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Clears the op state (last accesses + op modes) for a package but not
+ * the historical state.
+ *
+ * @param packageName The package to reset.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_APPOPS)
+ public void resetPackageOpsNoHistory(@NonNull String packageName) {
+ try {
+ mService.resetPackageOpsNoHistory(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Start collection of noted appops on this thread.
*
* <p>Called at the beginning of a two way binder transaction.
@@ -8771,7 +8906,7 @@
packageName = "android";
}
- // check it the appops needs to be collected and cache result
+ // check if the appops needs to be collected and cache result
if (sAppOpsToNote[op] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) {
boolean shouldCollectNotes;
try {
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 5e032f0..a3d0cf2 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -18,12 +18,17 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.AttributionSource;
+import android.os.IBinder;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.TriFunction;
/**
* App ops service local interface.
@@ -76,6 +81,55 @@
@Nullable String message, boolean shouldCollectMessage,
@NonNull HeptFunction<Integer, Integer, String, String, Boolean, String, Boolean,
Integer> superImpl);
+
+ /**
+ * Allows overriding note proxy operation behavior.
+ *
+ * @param code The op code to note.
+ * @param attributionSource The permission identity of the caller.
+ * @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
+ * @param message The message in the async noted op
+ * @param shouldCollectMessage whether to collect messages
+ * @param skipProxyOperation Whether to skip the proxy portion of the operation
+ * @param superImpl The super implementation.
+ * @return The app op note result.
+ */
+ int noteProxyOperation(int code, @NonNull AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, boolean skipProxyOperation,
+ @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean,
+ Boolean, Integer> superImpl);
+
+ /**
+ * Allows overriding start proxy operation behavior.
+ *
+ * @param code The op code to start.
+ * @param attributionSource The permission identity of the caller.
+ * @param startIfModeDefault Whether to start the op of the mode is default.
+ * @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
+ * @param message The message in the async noted op
+ * @param shouldCollectMessage whether to collect messages
+ * @param skipProxyOperation Whether to skip the proxy portion of the operation
+ * @param superImpl The super implementation.
+ * @return The app op note result.
+ */
+ int startProxyOperation(IBinder token, int code,
+ @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation, @NonNull OctFunction<IBinder, Integer,
+ AttributionSource, Boolean, Boolean, String, Boolean, Boolean,
+ Integer> superImpl);
+
+ /**
+ * Allows overriding finish proxy op.
+ *
+ * @param clientId Client state token.
+ * @param code The op code to finish.
+ * @param attributionSource The permission identity of the caller.
+ */
+ void finishProxyOperation(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource,
+ @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl);
}
/**
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 477e96b..9da2581 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -16,14 +16,16 @@
package android.app;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Build;
import android.os.Bundle;
-import android.os.PowerWhitelistManager;
-import android.os.PowerWhitelistManager.ReasonCode;
-import android.os.PowerWhitelistManager.TempAllowListType;
+import android.os.PowerExemptionManager;
+import android.os.PowerExemptionManager.ReasonCode;
+import android.os.PowerExemptionManager.TempAllowListType;
/**
* Helper class for building an options Bundle that can be used with
@@ -35,8 +37,7 @@
public class BroadcastOptions {
private long mTemporaryAppAllowlistDuration;
private @TempAllowListType int mTemporaryAppAllowlistType;
- private @ReasonCode int mTemporaryAppAllowlistReasonCode =
- PowerWhitelistManager.REASON_UNKNOWN;
+ private @ReasonCode int mTemporaryAppAllowlistReasonCode;
private @Nullable String mTemporaryAppAllowlistReason;
private int mMinManifestReceiverApiLevel = 0;
private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
@@ -47,59 +48,59 @@
* How long to temporarily put an app on the power allowlist when executing this broadcast
* to it.
*/
- static final String KEY_TEMPORARY_APP_ALLOWLIST_DURATION
+ private static final String KEY_TEMPORARY_APP_ALLOWLIST_DURATION
= "android:broadcast.temporaryAppAllowlistDuration";
- static final String KEY_TEMPORARY_APP_ALLOWLIST_TYPE
+ private static final String KEY_TEMPORARY_APP_ALLOWLIST_TYPE
= "android:broadcast.temporaryAppAllowlistType";
- static final String KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE =
+ private static final String KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE =
"android:broadcast.temporaryAppAllowlistReasonCode";
- static final String KEY_TEMPORARY_APP_ALLOWLIST_REASON =
+ private static final String KEY_TEMPORARY_APP_ALLOWLIST_REASON =
"android:broadcast.temporaryAppAllowlistReason";
/**
* Corresponds to {@link #setMinManifestReceiverApiLevel}.
*/
- static final String KEY_MIN_MANIFEST_RECEIVER_API_LEVEL
+ private static final String KEY_MIN_MANIFEST_RECEIVER_API_LEVEL
= "android:broadcast.minManifestReceiverApiLevel";
/**
* Corresponds to {@link #setMaxManifestReceiverApiLevel}.
*/
- static final String KEY_MAX_MANIFEST_RECEIVER_API_LEVEL
+ private static final String KEY_MAX_MANIFEST_RECEIVER_API_LEVEL
= "android:broadcast.maxManifestReceiverApiLevel";
/**
* Corresponds to {@link #setDontSendToRestrictedApps}.
*/
- static final String KEY_DONT_SEND_TO_RESTRICTED_APPS =
+ private static final String KEY_DONT_SEND_TO_RESTRICTED_APPS =
"android:broadcast.dontSendToRestrictedApps";
/**
* Corresponds to {@link #setBackgroundActivityStartsAllowed}.
*/
- static final String KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS =
+ private static final String KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS =
"android:broadcast.allowBackgroundActivityStarts";
/**
* @hide
- * @deprecated Use {@link android.os.PowerWhitelistManager#
- * TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED} instead.
+ * @deprecated Use {@link android.os.PowerExemptionManager#
+ * TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED} instead.
*/
@Deprecated
public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED =
- PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+ PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
/**
* @hide
- * @deprecated Use {@link android.os.PowerWhitelistManager#
- * TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} instead.
+ * @deprecated Use {@link android.os.PowerExemptionManager#
+ * TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} instead.
*/
@Deprecated
public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED =
- PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
+ PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
public static BroadcastOptions makeBasic() {
BroadcastOptions opts = new BroadcastOptions();
@@ -107,15 +108,22 @@
}
private BroadcastOptions() {
+ resetTemporaryAppAllowlist();
}
/** @hide */
- public BroadcastOptions(Bundle opts) {
- mTemporaryAppAllowlistDuration = opts.getLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION);
- mTemporaryAppAllowlistType = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE);
- mTemporaryAppAllowlistReasonCode = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE,
- PowerWhitelistManager.REASON_UNKNOWN);
- mTemporaryAppAllowlistReason = opts.getString(KEY_TEMPORARY_APP_ALLOWLIST_REASON);
+ @TestApi
+ public BroadcastOptions(@NonNull Bundle opts) {
+ // Match the logic in toBundle().
+ if (opts.containsKey(KEY_TEMPORARY_APP_ALLOWLIST_DURATION)) {
+ mTemporaryAppAllowlistDuration = opts.getLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION);
+ mTemporaryAppAllowlistType = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE);
+ mTemporaryAppAllowlistReasonCode = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE,
+ PowerExemptionManager.REASON_UNKNOWN);
+ mTemporaryAppAllowlistReason = opts.getString(KEY_TEMPORARY_APP_ALLOWLIST_REASON);
+ } else {
+ resetTemporaryAppAllowlist();
+ }
mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0);
mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
Build.VERSION_CODES.CUR_DEVELOPMENT);
@@ -136,18 +144,21 @@
android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND})
public void setTemporaryAppWhitelistDuration(long duration) {
setTemporaryAppAllowlist(duration,
- PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
- PowerWhitelistManager.REASON_UNKNOWN, null);
+ PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ PowerExemptionManager.REASON_UNKNOWN, null);
}
/**
* Set a duration for which the system should temporary place an application on the
* power allowlist when this broadcast is being delivered to it, specify the temp allowlist
* type.
- * @param duration the duration in milliseconds; 0 means to not place on allowlist.
- * @param type one of {@link TempAllowListType}
+ * @param duration the duration in milliseconds.
+ * 0 means to not place on allowlist, and clears previous call to this method.
+ * @param type one of {@link TempAllowListType}.
+ * {@link PowerExemptionManager#TEMPORARY_ALLOW_LIST_TYPE_NONE} means
+ * to not place on allowlist, and clears previous call to this method.
* @param reasonCode one of {@link ReasonCode}, use
- * {@link PowerWhitelistManager#REASON_UNKNOWN} if not sure.
+ * {@link PowerExemptionManager#REASON_UNKNOWN} if not sure.
* @param reason A human-readable reason explaining why the app is temp allowlisted. Only
* used for logging purposes. Could be null or empty string.
*/
@@ -160,12 +171,30 @@
mTemporaryAppAllowlistType = type;
mTemporaryAppAllowlistReasonCode = reasonCode;
mTemporaryAppAllowlistReason = reason;
+
+ if (!isTemporaryAppAllowlistSet()) {
+ resetTemporaryAppAllowlist();
+ }
+ }
+
+ private boolean isTemporaryAppAllowlistSet() {
+ return mTemporaryAppAllowlistDuration > 0
+ && mTemporaryAppAllowlistType
+ != PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
+ }
+
+ private void resetTemporaryAppAllowlist() {
+ mTemporaryAppAllowlistDuration = 0;
+ mTemporaryAppAllowlistType = PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
+ mTemporaryAppAllowlistReasonCode = PowerExemptionManager.REASON_UNKNOWN;
+ mTemporaryAppAllowlistReason = null;
}
/**
* Return {@link #setTemporaryAppAllowlist}.
* @hide
*/
+ @TestApi
public long getTemporaryAppAllowlistDuration() {
return mTemporaryAppAllowlistDuration;
}
@@ -174,6 +203,7 @@
* Return {@link #mTemporaryAppAllowlistType}.
* @hide
*/
+ @TestApi
public @TempAllowListType int getTemporaryAppAllowlistType() {
return mTemporaryAppAllowlistType;
}
@@ -182,6 +212,7 @@
* Return {@link #mTemporaryAppAllowlistReasonCode}.
* @hide
*/
+ @TestApi
public @ReasonCode int getTemporaryAppAllowlistReasonCode() {
return mTemporaryAppAllowlistReasonCode;
}
@@ -190,6 +221,7 @@
* Return {@link #mTemporaryAppAllowlistReason}.
* @hide
*/
+ @TestApi
public @Nullable String getTemporaryAppAllowlistReason() {
return mTemporaryAppAllowlistReason;
}
@@ -276,16 +308,10 @@
*/
public Bundle toBundle() {
Bundle b = new Bundle();
- if (mTemporaryAppAllowlistDuration > 0) {
+ if (isTemporaryAppAllowlistSet()) {
b.putLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION, mTemporaryAppAllowlistDuration);
- }
- if (mTemporaryAppAllowlistType != 0) {
b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE, mTemporaryAppAllowlistType);
- }
- if (mTemporaryAppAllowlistReasonCode != PowerWhitelistManager.REASON_UNKNOWN) {
b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, mTemporaryAppAllowlistReasonCode);
- }
- if (mTemporaryAppAllowlistReason != null) {
b.putString(KEY_TEMPORARY_APP_ALLOWLIST_REASON, mTemporaryAppAllowlistReason);
}
if (mMinManifestReceiverApiLevel != 0) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 03e95fc..f8165e9 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -60,6 +60,7 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.content.AttributionSource;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -224,8 +225,8 @@
private final String mBasePackageName;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mOpPackageName;
-
private final @NonNull ContextParams mParams;
+ private @NonNull AttributionSource mAttributionSource;
private final @NonNull ResourcesManager mResourcesManager;
@UnsupportedAppUsage
@@ -467,13 +468,13 @@
/** @hide */
@Override
public String getOpPackageName() {
- return mOpPackageName != null ? mOpPackageName : getBasePackageName();
+ return mAttributionSource.getPackageName();
}
/** @hide */
@Override
public @Nullable String getAttributionTag() {
- return mParams.getAttributionTag();
+ return mAttributionSource.getAttributionTag();
}
@Override
@@ -482,6 +483,11 @@
}
@Override
+ public @NonNull AttributionSource getAttributionSource() {
+ return mAttributionSource;
+ }
+
+ @Override
public ApplicationInfo getApplicationInfo() {
if (mPackageInfo != null) {
return mPackageInfo.getApplicationInfo();
@@ -2074,13 +2080,7 @@
Log.v(TAG, "Treating renounced permission " + permission + " as denied");
return PERMISSION_DENIED;
}
-
- try {
- return ActivityManager.getService().checkPermissionWithToken(
- permission, pid, uid, callerToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return checkPermission(permission, pid, uid);
}
@Override
@@ -2415,8 +2415,10 @@
LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE);
if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, ContextParams.EMPTY, null,
- mToken, new UserHandle(UserHandle.getUserId(application.uid)),
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, ContextParams.EMPTY,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ null, mToken, new UserHandle(UserHandle.getUserId(application.uid)),
flags, null, null);
final int displayId = getDisplayId();
@@ -2446,15 +2448,19 @@
if (packageName.equals("system") || packageName.equals("android")) {
// The system resources are loaded in every application, so we can safely copy
// the context without reloading Resources.
- return new ContextImpl(this, mMainThread, mPackageInfo, mParams, null,
- mToken, user, flags, null, null);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ null, mToken, user, flags, null, null);
}
LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, mParams, null,
- mToken, user, flags, null, null);
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ null, mToken, user, flags, null, null);
final int displayId = getDisplayId();
final Integer overrideDisplayId = mForceDisplayOverrideInResources
@@ -2491,8 +2497,10 @@
final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
final String[] paths = mPackageInfo.getSplitPaths(splitName);
- final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
- mParams, splitName, mToken, mUser, mFlags, classLoader, null);
+ final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ splitName, mToken, mUser, mFlags, classLoader, null);
context.setResources(ResourcesManager.getInstance().getResources(
mToken,
@@ -2526,6 +2534,8 @@
}
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
mSplitName, mToken, mUser, mFlags, mClassLoader, null);
final int displayId = getDisplayId();
@@ -2544,6 +2554,8 @@
}
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
mSplitName, mToken, mUser, mFlags, mClassLoader, null);
final int displayId = display.getDisplayId();
@@ -2650,6 +2662,8 @@
@UiContext
ContextImpl createWindowContextBase(@NonNull IBinder token, @NonNull Display display) {
ContextImpl baseContext = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
mSplitName, token, mUser, mFlags, mClassLoader, null);
// Window contexts receive configurations directly from the server and as such do not
// need to override their display in ResourcesManager.
@@ -2695,9 +2709,10 @@
@NonNull
@Override
- public Context createContext(@NonNull ContextParams params) {
- return new ContextImpl(this, mMainThread, mPackageInfo, params, mSplitName,
- mToken, mUser, mFlags, mClassLoader, null);
+ public Context createContext(@NonNull ContextParams contextParams) {
+ return new ContextImpl(this, mMainThread, mPackageInfo, contextParams,
+ contextParams.getAttributionTag(), contextParams.getNextAttributionSource(),
+ mSplitName, mToken, mUser, mFlags, mClassLoader, null);
}
@Override
@@ -2710,16 +2725,20 @@
public Context createDeviceProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mParams, mSplitName,
- mToken, mUser, flags, mClassLoader, null);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ mSplitName, mToken, mUser, flags, mClassLoader, null);
}
@Override
public Context createCredentialProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
| Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mParams, mSplitName,
- mToken, mUser, flags, mClassLoader, null);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mParams,
+ mAttributionSource.getAttributionTag(),
+ mAttributionSource.getNext(),
+ mSplitName, mToken, mUser, flags, mClassLoader, null);
}
@Override
@@ -2893,7 +2912,7 @@
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
- ContextParams.EMPTY, null, null, null, 0, null, null);
+ ContextParams.EMPTY, null, null, null, null, null, 0, null, null);
context.setResources(packageInfo.getResources());
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetrics());
@@ -2911,7 +2930,7 @@
static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
final LoadedApk packageInfo = systemContext.mPackageInfo;
ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo,
- ContextParams.EMPTY, null, null, null, 0, null, null);
+ ContextParams.EMPTY, null, null, null, null, null, 0, null, null);
context.setResources(createResources(null, packageInfo, null, displayId, null,
packageInfo.getCompatibilityInfo(), null));
context.updateDisplay(displayId);
@@ -2936,7 +2955,7 @@
String opPackageName) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
- ContextParams.EMPTY, null, null, null, 0, null, opPackageName);
+ ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName);
context.setResources(packageInfo.getResources());
context.mContextType = isSystemOrSystemUI(context) ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
: CONTEXT_TYPE_NON_UI;
@@ -2966,7 +2985,7 @@
}
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY,
- activityInfo.splitName, activityToken, null, 0, classLoader, null);
+ null, null, activityInfo.splitName, activityToken, null, 0, classLoader, null);
context.mContextType = CONTEXT_TYPE_ACTIVITY;
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
@@ -2999,6 +3018,7 @@
private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
@NonNull LoadedApk packageInfo, @NonNull ContextParams params,
+ @Nullable String attributionTag, @Nullable AttributionSource nextAttributionSource,
@Nullable String splitName, @Nullable IBinder token, @Nullable UserHandle user,
int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
mOuterContext = this;
@@ -3054,9 +3074,27 @@
mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;
mParams = Objects.requireNonNull(params);
+ initializeAttributionSource(attributionTag, nextAttributionSource);
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
+ private void initializeAttributionSource(@Nullable String attributionTag,
+ @Nullable AttributionSource nextAttributionSource) {
+ mAttributionSource = new AttributionSource(Process.myUid(), mOpPackageName,
+ attributionTag, nextAttributionSource);
+ // If we want to access protected data on behalf of another app we need to
+ // tell the OS that we opt in to participate in the attribution chain.
+ if (nextAttributionSource != null) {
+ // If an app happened to stub the internal OS for testing the registration method
+ // can return null. In this case we keep the current untrusted attribution source.
+ final AttributionSource attributionSource = getSystemService(PermissionManager.class)
+ .registerAttributionSource(mAttributionSource);
+ if (attributionSource != null) {
+ mAttributionSource = attributionSource;
+ }
+ }
+ }
+
void setResources(Resources r) {
if (r instanceof CompatResources) {
((CompatResources) r).setContext(this);
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 47de040..5964f71 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -135,4 +135,20 @@
throw e.rethrowFromSystemServer();
}
}
+ /**
+ * Returns a list of supported game modes for a given package.
+ * <p>
+ * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public @GameMode int[] getAvailableGameModes(@NonNull String packageName) {
+ try {
+ return mService.getAvailableGameModes(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 4c2433c..81e5e1d 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -475,8 +475,6 @@
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean isTopOfTask(in IBinder token);
void bootAnimationComplete();
- int checkPermissionWithToken(in String permission, int pid, int uid,
- in IBinder callerToken);
@UnsupportedAppUsage
void registerTaskStackListener(in ITaskStackListener listener);
void unregisterTaskStackListener(in ITaskStackListener listener);
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index c8e1478..4bf8a3f 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -22,4 +22,5 @@
interface IGameManagerService {
int getGameMode(String packageName, int userId);
void setGameMode(String packageName, int gameMode, int userId);
+ int[] getAvailableGameModes(String packageName);
}
\ No newline at end of file
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index bda2fa9..a5f8f10 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -104,7 +104,6 @@
void createConversationNotificationChannelForPackage(String pkg, int uid, in NotificationChannel parentChannel, String conversationId);
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, String conversationId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, String channelId);
- void deleteConversationNotificationChannels(String pkg, int uid, String conversationId);
ParceledListSlice getNotificationChannels(String callingPkg, String targetPkg, int userId);
ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 420ec08..2fbea28 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -38,6 +38,7 @@
import android.annotation.StringRes;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -130,6 +131,52 @@
private static final String TAG = "Notification";
/**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ FOREGROUND_SERVICE_DEFAULT,
+ FOREGROUND_SERVICE_IMMEDIATE,
+ FOREGROUND_SERVICE_DEFERRED
+ })
+ public @interface ServiceNotificationPolicy {};
+
+ /**
+ * If the Notification associated with starting a foreground service has been
+ * built using setForegroundServiceBehavior() with this behavior, display of
+ * the notification will usually be suppressed for a short time to avoid visual
+ * disturbances to the user.
+ * @see Notification.Builder#setForegroundServiceBehavior(int)
+ * @see #FOREGROUND_SERVICE_IMMEDIATE
+ * @see #FOREGROUND_SERVICE_DEFERRED
+ */
+ public static final @ServiceNotificationPolicy int FOREGROUND_SERVICE_DEFAULT = 0;
+
+ /**
+ * If the Notification associated with starting a foreground service has been
+ * built using setForegroundServiceBehavior() with this behavior, display of
+ * the notification will be immediate even if the default behavior would be
+ * to defer visibility for a short time.
+ * @see Notification.Builder#setForegroundServiceBehavior(int)
+ * @see #FOREGROUND_SERVICE_DEFAULT
+ * @see #FOREGROUND_SERVICE_DEFERRED
+ */
+ public static final @ServiceNotificationPolicy int FOREGROUND_SERVICE_IMMEDIATE = 1;
+
+ /**
+ * If the Notification associated with starting a foreground service has been
+ * built using setForegroundServiceBehavior() with this behavior, display of
+ * the notification will usually be suppressed for a short time to avoid visual
+ * disturbances to the user.
+ * @see Notification.Builder#setForegroundServiceBehavior(int)
+ * @see #FOREGROUND_SERVICE_DEFAULT
+ * @see #FOREGROUND_SERVICE_IMMEDIATE
+ */
+ public static final @ServiceNotificationPolicy int FOREGROUND_SERVICE_DEFERRED = 2;
+
+ private int mFgsDeferBehavior;
+
+ /**
* An activity that provides a user interface for adjusting notification preferences for its
* containing application.
*/
@@ -644,11 +691,6 @@
*/
public static final int FLAG_BUBBLE = 0x00001000;
- /**
- * @hide
- */
- public static final int FLAG_IMMEDIATE_FGS_DISPLAY = 0x00002000;
-
private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
@@ -658,8 +700,7 @@
@IntDef(flag = true, prefix = { "FLAG_" }, value = {FLAG_SHOW_LIGHTS, FLAG_ONGOING_EVENT,
FLAG_INSISTENT, FLAG_ONLY_ALERT_ONCE,
FLAG_AUTO_CANCEL, FLAG_NO_CLEAR, FLAG_FOREGROUND_SERVICE, FLAG_HIGH_PRIORITY,
- FLAG_LOCAL_ONLY, FLAG_GROUP_SUMMARY, FLAG_AUTOGROUP_SUMMARY, FLAG_BUBBLE,
- FLAG_IMMEDIATE_FGS_DISPLAY})
+ FLAG_LOCAL_ONLY, FLAG_GROUP_SUMMARY, FLAG_AUTOGROUP_SUMMARY, FLAG_BUBBLE})
@Retention(RetentionPolicy.SOURCE)
public @interface NotificationFlags{};
@@ -2549,6 +2590,8 @@
}
mAllowSystemGeneratedContextualActions = parcel.readBoolean();
+
+ mFgsDeferBehavior = parcel.readInt();
}
@Override
@@ -2664,6 +2707,7 @@
that.mBadgeIcon = this.mBadgeIcon;
that.mSettingsText = this.mSettingsText;
that.mGroupAlertBehavior = this.mGroupAlertBehavior;
+ that.mFgsDeferBehavior = this.mFgsDeferBehavior;
that.mBubbleMetadata = this.mBubbleMetadata;
that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;
@@ -3063,6 +3107,8 @@
parcel.writeBoolean(mAllowSystemGeneratedContextualActions);
+ parcel.writeInt(mFgsDeferBehavior);
+
// mUsesStandardHeader is not written because it should be recomputed in listeners
}
@@ -4531,10 +4577,40 @@
* foreground service is shown as soon as the service's {@code startForeground()}
* method is called, even if the system's UI policy might otherwise defer
* its visibility to a later time.
+ * @deprecated Use setForegroundServiceBehavior(int) instead
*/
+ @Deprecated
@NonNull
public Builder setShowForegroundImmediately(boolean showImmediately) {
- setFlag(FLAG_IMMEDIATE_FGS_DISPLAY, showImmediately);
+ setForegroundServiceBehavior(showImmediately
+ ? FOREGROUND_SERVICE_IMMEDIATE
+ : FOREGROUND_SERVICE_DEFAULT);
+ return this;
+ }
+
+ /**
+ * Specify a desired visibility policy for a Notification associated with a
+ * foreground service. By default, the system can choose to defer
+ * visibility of the notification for a short time after the service is
+ * started. Pass
+ * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE BEHAVIOR_IMMEDIATE_DISPLAY}
+ * to this method in order to guarantee that visibility is never deferred. Pass
+ * {@link Notification#FOREGROUND_SERVICE_DEFERRED BEHAVIOR_DEFERRED_DISPLAY}
+ * to request that visibility is deferred whenever possible.
+ *
+ * <p class="note">Note that deferred visibility is not guaranteed. There
+ * may be some circumstances under which the system will show the foreground
+ * service's associated Notification immediately even when the app has used
+ * this method to explicitly request deferred display.</p>
+ * @param behavior One of
+ * {@link Notification#FOREGROUND_SERVICE_DEFAULT BEHAVIOR_DEFAULT},
+ * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE BEHAVIOR_IMMEDIATE_DISPLAY},
+ * or {@link Notification#FOREGROUND_SERVICE_DEFERRED BEHAVIOR_DEFERRED_DISPLAY}
+ * @return
+ */
+ @NonNull
+ public Builder setForegroundServiceBehavior(int behavior) {
+ mN.mFgsDeferBehavior = behavior;
return this;
}
@@ -5284,6 +5360,11 @@
// Use different highlighted colors except when low-priority mode prevents that
if (!p.mReduceHighlights) {
pillColor = getAccentTertiaryColor(p);
+ // TODO(b/183710694): The accent tertiary is currently too bright in dark mode, so
+ // we need to pick a contrasting color.
+ textColor = ColorUtils.setAlphaComponent(
+ ContrastColorUtil.resolvePrimaryColor(mContext, pillColor, mInNightMode),
+ 0xFF);
}
contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
@@ -6717,31 +6798,60 @@
* immediately when tied to a foreground service, even if the system might generally
* avoid showing the notifications for short-lived foreground service lifetimes.
*
- * Immediate visibility of the Notification is recommended when:
+ * Immediate visibility of the Notification is indicated when:
* <ul>
* <li>The app specifically indicated it with
- * {@link Notification.Builder#setShowForegroundImmediately(boolean)
- * setShowForegroundImmediately(true)}</li>
+ * {@link Notification.Builder#setForegroundServiceBehavior(int)
+ * setForegroundServiceBehavior(BEHAVIOR_IMMEDIATE_DISPLAY)}</li>
* <li>It is a media notification or has an associated media session</li>
* <li>It is a call or navigation notification</li>
* <li>It provides additional action affordances</li>
* </ul>
- * @return whether this notification should always be displayed immediately when
+ *
+ * If the app has specified
+ * {@code NotificationBuilder.setForegroundServiceBehavior(BEHAVIOR_DEFERRED_DISPLAY)}
+ * then this method will return {@code false} and notification visibility will be
+ * deferred following the service's transition to the foreground state even in the
+ * circumstances described above.
+ *
+ * @return whether this notification should be displayed immediately when
* its associated service transitions to the foreground state
* @hide
*/
+ @TestApi
public boolean shouldShowForegroundImmediately() {
- if ((flags & Notification.FLAG_IMMEDIATE_FGS_DISPLAY) != 0
- || isMediaNotification() || hasMediaSession()
- || CATEGORY_CALL.equals(category)
- || CATEGORY_NAVIGATION.equals(category)
- || (actions != null && actions.length > 0)) {
+ // Has the app demanded immediate display?
+ if (mFgsDeferBehavior == FOREGROUND_SERVICE_IMMEDIATE) {
return true;
}
+
+ // Has the app demanded deferred display?
+ if (mFgsDeferBehavior == FOREGROUND_SERVICE_DEFERRED) {
+ return false;
+ }
+
+ // We show these sorts of notifications immediately in the absence of
+ // any explicit app declaration
+ if (isMediaNotification() || hasMediaSession()
+ || CATEGORY_CALL.equals(category)
+ || CATEGORY_NAVIGATION.equals(category)
+ || (actions != null && actions.length > 0)) {
+ return true;
+ }
+
+ // No extenuating circumstances: defer visibility
return false;
}
/**
+ * Has forced deferral for FGS purposes been specified?
+ * @hide
+ */
+ public boolean isForegroundDisplayForceDeferred() {
+ return FOREGROUND_SERVICE_DEFERRED == mFgsDeferBehavior;
+ }
+
+ /**
* @return whether this notification has a media session attached
* @hide
*/
@@ -12276,19 +12386,13 @@
}
public StandardTemplateParams decorationType(int decorationType) {
- // These fields are removed by the decoration process, and thus would not show anyway;
- // hiding them is a minimal time/space optimization.
- hideAppName(true);
hideTitle(true);
- hideSubText(true);
- hideTime(true);
// Minimally decorated custom views do not show certain pieces of chrome that have
// always been shown when using DecoratedCustomViewStyle.
boolean hideOtherFields = decorationType <= DECORATION_MINIMAL;
hideLargeIcon(hideOtherFields);
hideProgress(hideOtherFields);
hideActions(hideOtherFields);
- hideSnoozeButton(hideOtherFields);
return this;
}
}
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 747a2de..da64dcd 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -524,6 +524,16 @@
"android.app.action.OPERATION_SAFETY_STATE_CHANGED";
/**
+ * Broadcast action: notify the profile owner on an organization-owned device that it needs to
+ * acknowledge device compliance.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED =
+ "android.app.action.COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED";
+
+ /**
* An {@code int} extra specifying an {@link OperationSafetyReason}.
*
* @hide
@@ -1116,6 +1126,29 @@
onOperationSafetyStateChanged(context, reason, isSafe);
}
+ /**
+ * Called to notify a profile owner of an organization-owned device that it needs to acknowledge
+ * device compliance to allow the user to turn the profile off if needed according to the
+ * maximum profile time off policy.
+ *
+ * Default implementation acknowledges compliance immediately. DPC may prefer to override this
+ * implementation to delay acknowledgement until a successful policy sync. Until compliance is
+ * acknowledged the user is still free to turn the profile off, but the timer won't be reset,
+ * so personal apps will be suspended sooner. This callback is delivered using a foreground
+ * broadcast and should be handled quickly.
+ *
+ * @param context the running context as per {@link #onReceive}
+ * @param intent The received intent as per {@link #onReceive}.
+ *
+ * @see DevicePolicyManager#acknowledgeDeviceCompliant()
+ * @see DevicePolicyManager#isComplianceAcknowledgementRequired()
+ * @see DevicePolicyManager#setManagedProfileMaximumTimeOff(ComponentName, long)
+ */
+ public void onComplianceAcknowledgementRequired(
+ @NonNull Context context, @NonNull Intent intent) {
+ getManager(context).acknowledgeDeviceCompliant();
+ }
+
private boolean hasRequiredExtra(Intent intent, String extra) {
if (intent.hasExtra(extra)) return true;
@@ -1204,6 +1237,8 @@
intent.getParcelableExtra(Intent.EXTRA_USER));
} else if (ACTION_OPERATION_SAFETY_STATE_CHANGED.equals(action)) {
onOperationSafetyStateChanged(context, intent);
+ } else if (ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED.equals(action)) {
+ onComplianceAcknowledgementRequired(context, intent);
}
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d3534f9..89d08cc 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1675,6 +1675,29 @@
})
public @interface PasswordComplexity {}
+ /** Indicates that nearby streaming is disabled. */
+ public static final int NEARBY_STREAMING_DISABLED = 0;
+
+ /** Indicates that nearby streaming is enabled. */
+ public static final int NEARBY_STREAMING_ENABLED = 1;
+
+ /**
+ * Indicates that nearby streaming is enabled only to devices offering a comparable level of
+ * security, with the same authenticated managed account.
+ */
+ public static final int NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY = 2;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"NEARBY_STREAMING_"}, value = {
+ NEARBY_STREAMING_DISABLED,
+ NEARBY_STREAMING_ENABLED,
+ NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY,
+ })
+ public @interface NearbyStreamingPolicy {}
+
/**
* Activity action: have the user enter a new password for the parent profile.
* If the intent is launched from within a managed profile, this will trigger
@@ -3306,6 +3329,41 @@
"android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
/**
+ * A {@code boolean} metadata to be included in a mainline module's {@code <application>}
+ * manifest element, which declares that the module should be considered a required app for
+ * managed users.
+ * <p>Being declared as a required app prevents removal of this package during the
+ * provisioning process.
+ * @hide
+ */
+ @SystemApi
+ public static final String REQUIRED_APP_MANAGED_USER = "android.app.REQUIRED_APP_MANAGED_USER";
+
+ /**
+ * A {@code boolean} metadata to be included in a mainline module's {@code <application>}
+ * manifest element, which declares that the module should be considered a required app for
+ * managed devices.
+ * <p>Being declared as a required app prevents removal of this package during the
+ * provisioning process.
+ * @hide
+ */
+ @SystemApi
+ public static final String REQUIRED_APP_MANAGED_DEVICE =
+ "android.app.REQUIRED_APP_MANAGED_DEVICE";
+
+ /**
+ * A {@code boolean} metadata to be included in a mainline module's {@code <application>}
+ * manifest element, which declares that the module should be considered a required app for
+ * managed profiles.
+ * <p>Being declared as a required app prevents removal of this package during the
+ * provisioning process.
+ * @hide
+ */
+ @SystemApi
+ public static final String REQUIRED_APP_MANAGED_PROFILE =
+ "android.app.REQUIRED_APP_MANAGED_PROFILE";
+
+ /**
* Called by an application that is administering the device to set the password restrictions it
* is imposing. After setting this, the user will not be able to enter a new password that is
* not at least as restrictive as what has been set. Note that the current password will remain
@@ -7128,6 +7186,77 @@
}
/**
+ * Called by a device/profile owner to set nearby notification streaming policy. Notification
+ * streaming is sending notification data from pre-installed apps to nearby devices.
+ *
+ * @param policy One of the {@code NearbyStreamingPolicy} constants.
+ * @throws SecurityException if caller is not a device or profile owner
+ */
+ public void setNearbyNotificationStreamingPolicy(@NearbyStreamingPolicy int policy) {
+ throwIfParentInstance("setNearbyNotificationStreamingPolicy");
+ if (mService == null) {
+ return;
+ }
+ try {
+ mService.setNearbyNotificationStreamingPolicy(policy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current runtime nearby notification streaming policy set by the device or profile
+ * owner. The default is {@link #NEARBY_STREAMING_DISABLED}.
+ */
+ public @NearbyStreamingPolicy int getNearbyNotificationStreamingPolicy() {
+ throwIfParentInstance("getNearbyNotificationStreamingPolicy");
+ if (mService == null) {
+ return NEARBY_STREAMING_DISABLED;
+ }
+ try {
+ return mService.getNearbyNotificationStreamingPolicy();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called by a device/profile owner to set nearby app streaming policy. App streaming is when
+ * the device starts an app on a virtual display and sends a video stream of the app to nearby
+ * devices.
+ *
+ * @param policy One of the {@code NearbyStreamingPolicy} constants.
+ * @throws SecurityException if caller is not a device or profile owner.
+ */
+ public void setNearbyAppStreamingPolicy(@NearbyStreamingPolicy int policy) {
+ throwIfParentInstance("setNearbyAppStreamingPolicy");
+ if (mService == null) {
+ return;
+ }
+ try {
+ mService.setNearbyAppStreamingPolicy(policy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current runtime nearby app streaming policy set by the device or profile owner.
+ * The default is {@link #NEARBY_STREAMING_DISABLED}.
+ */
+ public @NearbyStreamingPolicy int getNearbyAppStreamingPolicy() {
+ throwIfParentInstance("getNearbyAppStreamingPolicy");
+ if (mService == null) {
+ return NEARBY_STREAMING_DISABLED;
+ }
+ try {
+ return mService.getNearbyAppStreamingPolicy();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Called by a device owner, or alternatively a profile owner from Android 8.0 (API level 26) or
* higher, to set whether auto time is required. If auto time is required, no user will be able
* set the date and time and network date and time will be used.
@@ -13374,6 +13503,63 @@
}
/**
+ * Called by a profile owner of an organization-owned managed profile to acknowledge that the
+ * device is compliant and the user can turn the profile off if needed according to the maximum
+ * time off policy.
+ *
+ * This method should be called when the device is deemed compliant after getting
+ * {@link DeviceAdminReceiver#onComplianceAcknowledgementRequired(Context, Intent)} callback in
+ * case it is overridden. Before this method is called the user is still free to turn the
+ * profile off, but the timer won't be reset, so personal apps will be suspended sooner.
+ *
+ * DPCs only need acknowledging device compliance if they override
+ * {@link DeviceAdminReceiver#onComplianceAcknowledgementRequired(Context, Intent)}, otherwise
+ * compliance is acknowledged automatically.
+ *
+ * @throws IllegalStateException if the user isn't unlocked
+ * @see #isComplianceAcknowledgementRequired()
+ * @see #setManagedProfileMaximumTimeOff(ComponentName, long)
+ * @see DeviceAdminReceiver#onComplianceAcknowledgementRequired(Context, Intent)
+ */
+ public void acknowledgeDeviceCompliant() {
+ throwIfParentInstance("acknowledgeDeviceCompliant");
+ if (mService != null) {
+ try {
+ mService.acknowledgeDeviceCompliant();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Called by a profile owner of an organization-owned managed profile to query whether it needs
+ * to acknowledge device compliance to allow the user to turn the profile off if needed
+ * according to the maximum profile time off policy.
+ *
+ * Normally when acknowledgement is needed the DPC gets a
+ * {@link DeviceAdminReceiver#onComplianceAcknowledgementRequired(Context, Intent)} callback.
+ * But if the callback was not delivered or handled for some reason, this method can be used to
+ * verify if acknowledgement is needed.
+ *
+ * @throws IllegalStateException if the user isn't unlocked
+ * @see #acknowledgeDeviceCompliant()
+ * @see #setManagedProfileMaximumTimeOff(ComponentName, long)
+ * @see DeviceAdminReceiver#onComplianceAcknowledgementRequired(Context, Intent)
+ */
+ public boolean isComplianceAcknowledgementRequired() {
+ throwIfParentInstance("isComplianceAcknowledgementRequired");
+ if (mService != null) {
+ try {
+ return mService.isComplianceAcknowledgementRequired();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns {@code true} when {@code userId} has a profile owner that is capable of resetting
* password in RUNNING_LOCKED state. For that it should have at least one direct boot aware
* component and have an active password reset token. Can only be called by the system.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0ad92b7..8a8c69c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -132,6 +132,12 @@
void setScreenCaptureDisabled(in ComponentName who, boolean disabled, boolean parent);
boolean getScreenCaptureDisabled(in ComponentName who, int userHandle, boolean parent);
+ void setNearbyNotificationStreamingPolicy(int policy);
+ int getNearbyNotificationStreamingPolicy();
+
+ void setNearbyAppStreamingPolicy(int policy);
+ int getNearbyAppStreamingPolicy();
+
void setKeyguardDisabledFeatures(in ComponentName who, int which, boolean parent);
int getKeyguardDisabledFeatures(in ComponentName who, int userHandle, boolean parent);
@@ -495,6 +501,10 @@
long getManagedProfileMaximumTimeOff(in ComponentName admin);
void setManagedProfileMaximumTimeOff(in ComponentName admin, long timeoutMs);
+
+ void acknowledgeDeviceCompliant();
+ boolean isComplianceAcknowledgementRequired();
+
boolean canProfileOwnerResetPasswordWhenLocked(int userId);
void setNextOperationSafety(int operation, int reason);
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index ab38832..74e1ece 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -104,16 +104,15 @@
*
* @param packageName The package name of the app in question.
* @param overrides A map from changeId to the override applied for this change id.
- * @hide
*/
- @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG)
- public static void setPackageOverride(String packageName,
- Map<Long, PackageOverride> overrides) {
+ @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+ public static void setPackageOverride(@NonNull String packageName,
+ @NonNull Map<Long, PackageOverride> overrides) {
IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides);
try {
- platformCompat.setOverridesFromInstaller(config, packageName);
+ platformCompat.setOverridesOnReleaseBuilds(config, packageName);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index 9f97cd4..59b3555 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -17,8 +17,9 @@
package android.app.compat;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
-import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -32,15 +33,16 @@
*
* @hide
*/
-public class PackageOverride implements Parcelable {
+@SystemApi
+public final class PackageOverride {
+ /** @hide */
@IntDef({
VALUE_UNDEFINED,
VALUE_ENABLED,
VALUE_DISABLED
})
@Retention(RetentionPolicy.SOURCE)
- /** @hide */
public @interface EvaluatedOverride {
}
@@ -75,10 +77,6 @@
this.mEnabled = enabled;
}
- private PackageOverride(Parcel in) {
- this(in.readLong(), in.readLong(), in.readBoolean());
- }
-
/**
* Evaluate the override for the given {@code versionCode}. If no override is defined for
* the specified version code, {@link #VALUE_UNDEFINED} is returned.
@@ -114,25 +112,23 @@
}
/** Returns the enabled value for the override. */
- public boolean getEnabled() {
+ public boolean isEnabled() {
return mEnabled;
}
/** @hide */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** @hide */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(Parcel dest) {
dest.writeLong(mMinVersionCode);
dest.writeLong(mMaxVersionCode);
dest.writeBoolean(mEnabled);
}
/** @hide */
+ public static PackageOverride createFromParcel(Parcel in) {
+ return new PackageOverride(in.readLong(), in.readLong(), in.readBoolean());
+ }
+
+ /** @hide */
@Override
public String toString() {
if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) {
@@ -141,25 +137,10 @@
return String.format("[%d,%d,%b]", mMinVersionCode, mMaxVersionCode, mEnabled);
}
- /** @hide */
- public static final Creator<PackageOverride> CREATOR =
- new Creator<PackageOverride>() {
-
- @Override
- public PackageOverride createFromParcel(Parcel in) {
- return new PackageOverride(in);
- }
-
- @Override
- public PackageOverride[] newArray(int size) {
- return new PackageOverride[size];
- }
- };
-
/**
* Builder to construct a PackageOverride.
*/
- public static class Builder {
+ public static final class Builder {
private long mMinVersionCode = Long.MIN_VALUE;
private long mMaxVersionCode = Long.MAX_VALUE;
private boolean mEnabled;
@@ -169,6 +150,7 @@
*
* default value: {@code Long.MIN_VALUE}.
*/
+ @NonNull
public Builder setMinVersionCode(long minVersionCode) {
mMinVersionCode = minVersionCode;
return this;
@@ -179,6 +161,7 @@
*
* default value: {@code Long.MAX_VALUE}.
*/
+ @NonNull
public Builder setMaxVersionCode(long maxVersionCode) {
mMaxVersionCode = maxVersionCode;
return this;
@@ -189,6 +172,7 @@
*
* default value: {@code false}.
*/
+ @NonNull
public Builder setEnabled(boolean enabled) {
mEnabled = enabled;
return this;
@@ -200,6 +184,7 @@
* @throws IllegalArgumentException if {@code minVersionCode} is larger than
* {@code maxVersionCode}.
*/
+ @NonNull
public PackageOverride build() {
if (mMinVersionCode > mMaxVersionCode) {
throw new IllegalArgumentException("minVersionCode must not be larger than "
diff --git a/core/java/android/app/time/ExternalTimeSuggestion.java b/core/java/android/app/time/ExternalTimeSuggestion.java
index 61defb5..8e281c0 100644
--- a/core/java/android/app/time/ExternalTimeSuggestion.java
+++ b/core/java/android/app/time/ExternalTimeSuggestion.java
@@ -20,6 +20,7 @@
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.TimestampedValue;
@@ -67,6 +68,7 @@
*
* @hide
*/
+@SystemApi
public final class ExternalTimeSuggestion implements Parcelable {
public static final @NonNull Creator<ExternalTimeSuggestion> CREATOR =
@@ -129,7 +131,6 @@
/**
* Returns information that can be useful for debugging / logging. See {@link #addDebugInfo}.
- * {@hide}
*/
@NonNull
public List<String> getDebugInfo() {
diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java
index c71badb0..d6acb8c 100644
--- a/core/java/android/app/time/TimeManager.java
+++ b/core/java/android/app/time/TimeManager.java
@@ -262,7 +262,6 @@
* HAL. This time <em>may</em> be used to set the device system clock, depending on the device
* configuration and user settings. This method call is processed asynchronously.
* See {@link ExternalTimeSuggestion} for more details.
- * {@hide}
*/
@RequiresPermission(android.Manifest.permission.SUGGEST_EXTERNAL_TIME)
public void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSuggestion) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index a3d19ca..0be7b73 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -745,7 +745,6 @@
* Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
*/
BluetoothAdapter(IBluetoothManager managerService) {
-
if (managerService == null) {
throw new IllegalArgumentException("bluetooth manager service is null");
}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 4fb5577..632572d 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -567,6 +567,7 @@
* @return true if priority is set, false on error
* @hide
* @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)}
+ * @removed
*/
@Deprecated
@SystemApi
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 86bd8a2..2ce7156 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -425,6 +425,32 @@
mContext.getPackageName(), deviceAddress);
} catch (RemoteException e) {
ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class);
+ }
+ }
+
+ /**
+ * Associates given device with given app for the given user directly, without UI prompt.
+ *
+ * @return whether successful
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES)
+ public boolean associate(
+ @NonNull String packageName,
+ @NonNull MacAddress macAddress) {
+ if (!checkFeaturePresent()) {
+ return false;
+ }
+ Objects.requireNonNull(packageName, "package name cannot be null");
+ Objects.requireNonNull(macAddress, "mac address cannot be null");
+
+ UserHandle user = android.os.Process.myUserHandle();
+ try {
+ return mService.createAssociation(
+ packageName, macAddress.toString(), user.getIdentifier());
+ } catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 95d3515..83db358 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -51,4 +51,6 @@
void unregisterDevicePresenceListenerService(in String packageName, in String deviceAddress);
boolean canPairWithoutPrompt(in String packageName, in String deviceMacAddress, int userId);
+
+ boolean createAssociation(in String packageName, in String macAddress, int userId);
}
diff --git a/core/java/android/content/AttributionSource.aidl b/core/java/android/content/AttributionSource.aidl
new file mode 100644
index 0000000..10d5c27
--- /dev/null
+++ b/core/java/android/content/AttributionSource.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content;
+
+parcelable AttributionSource;
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
new file mode 100644
index 0000000..053bfc1
--- /dev/null
+++ b/core/java/android/content/AttributionSource.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.AppGlobals;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.permission.PermissionManager;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class represents a source to which access to permission protected data should be
+ * attributed. Attribution sources can be chained to represent cases where the protected
+ * data would flow through several applications. For example, app A may ask app B for
+ * contacts and in turn app B may ask app C for contacts. In this case, the attribution
+ * chain would be A -> B -> C and the data flow would be C -> B -> A. There are two
+ * main benefits of using the attribution source mechanism: avoid doing explicit permission
+ * checks on behalf of the calling app if you are accessing private data on their behalf
+ * to send back; avoid double data access blaming which happens as you check the calling
+ * app's permissions and when you access the data behind these permissions (for runtime
+ * permissions). Also if not explicitly blaming the caller the data access would be
+ * counted towards your app vs to the previous app where yours was just a proxy.
+ * <p>
+ * Every {@link Context} has an attribution source and you can get it via {@link
+ * Context#getAttributionSource()} representing itself, which is a chain of one. You
+ * can attribute work to another app, or more precisely to a chain of apps, through
+ * which the data you would be accessing would flow, via {@link Context#createContext(
+ * ContextParams)} plus specifying an attribution source for the next app to receive
+ * the protected data you are accessing via {@link AttributionSource.Builder#setNext(
+ * AttributionSource)}. Creating this attribution chain ensures that the datasource would
+ * check whether every app in the attribution chain has permission to access the data
+ * before releasing it. The datasource will also record appropriately that this data was
+ * accessed by the apps in the sequence if the data is behind a sensitive permission
+ * (e.g. dangerous). Again, this is useful if you are accessing the data on behalf of another
+ * app, for example a speech recognizer using the mic so it can provide recognition to
+ * a calling app.
+ * <p>
+ * You can create an attribution chain of you and any other app without any verification
+ * as this is something already available via the {@link android.app.AppOpsManager} APIs.
+ * This is supported to handle cases where you don't have access to the caller's attribution
+ * source and you can directly use the {@link AttributionSource.Builder} APIs. However,
+ * if the data flows through more than two apps (more than you access the data for the
+ * caller - which you cannot know ahead of time) you need to have a handle to the {@link
+ * AttributionSource} for the calling app's context in order to create an attribution context.
+ * This means you either need to have an API for the other app to send you its attribution
+ * source or use a platform API that pipes the callers attribution source.
+ * <p>
+ * You cannot forge an attribution chain without the participation of every app in the
+ * attribution chain (aside of the special case mentioned above). To create an attribution
+ * source that is trusted you need to create an attribution context that points to an
+ * attribution source that was explicitly created by the app that it refers to, recursively.
+ * <p>
+ * Since creating an attribution context leads to all permissions for apps in the attribution
+ * chain being checked, you need to expect getting a security exception when accessing
+ * permission protected APIs since some app in the chain may not have the permission.
+ */
+@Immutable
+// TODO: Codegen doesn't properly verify the class if the parcelling is inner class
+// TODO: Codegen doesn't allow overriding the constructor to change its visibility
+// TODO: Codegen applies method level annotations to argument vs the generated member (@SystemApi)
+// TODO: Codegen doesn't properly read/write IBinder members
+// TODO: Codegen doesn't properly handle Set arguments
+// @DataClass(genEqualsHashCode = true, genConstructor = false, genBuilder = true)
+public final class AttributionSource implements Parcelable {
+ /**
+ * @hide
+ */
+ static class RenouncedPermissionsParcelling implements Parcelling<Set<String>> {
+
+ @Override
+ public void parcel(Set<String> item, Parcel dest, int parcelFlags) {
+ if (item == null) {
+ dest.writeInt(-1);
+ } else {
+ dest.writeInt(item.size());
+ for (String permission : item) {
+ dest.writeString8(permission);
+ }
+ }
+ }
+
+ @Override
+ public Set<String> unparcel(Parcel source) {
+ final int size = source.readInt();
+ if (size < 0) {
+ return null;
+ }
+ final ArraySet<String> result = new ArraySet<>(size);
+ for (int i = 0; i < size; i++) {
+ result.add(source.readString8());
+ }
+ return result;
+ }
+ }
+
+ /**
+ * The UID that is accessing the permission protected data.
+ */
+ private final int mUid;
+
+ /**
+ * The package that is accessing the permission protected data.
+ */
+ private @Nullable String mPackageName = null;
+
+ /**
+ * The attribution tag of the app accessing the permission protected data.
+ */
+ private @Nullable String mAttributionTag = null;
+
+ /**
+ * Unique token for that source.
+ *
+ * @hide
+ */
+ private @Nullable IBinder mToken = null;
+
+ /**
+ * Permissions that should be considered revoked regardless if granted.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
+ @DataClass.ParcelWith(RenouncedPermissionsParcelling.class)
+ private @Nullable Set<String> mRenouncedPermissions = null;
+
+ /**
+ * The next app to receive the permission protected data.
+ */
+ private @Nullable AttributionSource mNext = null;
+
+ /** @hide */
+ @TestApi
+ public AttributionSource(int uid, @Nullable String packageName,
+ @Nullable String attributionTag) {
+ this(uid, packageName, attributionTag, /*next*/ null);
+ }
+
+ /** @hide */
+ @TestApi
+ public AttributionSource(int uid, @Nullable String packageName,
+ @Nullable String attributionTag, @Nullable AttributionSource next) {
+ this(uid, packageName, attributionTag, /*token*/ null,
+ /*renouncedPermissions*/ null, next);
+ }
+
+ /** @hide */
+ public AttributionSource(@NonNull AttributionSource current,
+ @Nullable AttributionSource next) {
+ this(current.getUid(), current.getPackageName(), current.getAttributionTag(),
+ /*token*/ null, /*renouncedPermissions*/ null, next);
+ }
+
+ /** @hide */
+ public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) {
+ return new AttributionSource(mUid, mPackageName, mAttributionTag, mToken,
+ mRenouncedPermissions, next);
+ }
+
+ /** @hide */
+ public AttributionSource withToken(@Nullable IBinder token) {
+ return new AttributionSource(mUid, mPackageName, mAttributionTag, token,
+ mRenouncedPermissions, mNext);
+ }
+
+ /**
+ * If you are handling an IPC and you don't trust the caller you need to validate
+ * whether the attribution source is one for the calling app to prevent the caller
+ * to pass you a source from another app without including themselves in the
+ * attribution chain.
+ *
+ * @throws SecurityException if the attribution source cannot be trusted to be
+ * from the caller.
+ */
+ public void enforceCallingUid() {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID && callingUid != mUid) {
+ throw new SecurityException("Calling uid: " + callingUid
+ + " doesn't match source uid: " + mUid);
+ }
+ // No need to check package as app ops manager does it already.
+ }
+
+ /**
+ * If you are handling an IPC and you don't trust the caller you need to validate
+ * whether the attribution source is one for the calling app to prevent the caller
+ * to pass you a source from another app without including themselves in the
+ * attribution chain.
+ *f
+ * @return if the attribution source cannot be trusted to be from the caller.
+ */
+ public boolean checkCallingUid() {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID && callingUid != mUid) {
+ return false;
+ }
+ // No need to check package as app ops manager does it already.
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ if (Build.IS_DEBUGGABLE) {
+ return "AttributionSource { " +
+ "uid = " + mUid + ", " +
+ "packageName = " + mPackageName + ", " +
+ "attributionTag = " + mAttributionTag + ", " +
+ "token = " + mToken + ", " +
+ "next = " + mNext +
+ " }";
+ }
+ return super.toString();
+ }
+
+ /**
+ * @return The next UID that would receive the permission protected data.
+ *
+ * @hide
+ */
+ public int getNextUid() {
+ if (mNext != null) {
+ return mNext.getUid();
+ }
+ return Process.INVALID_UID;
+ }
+
+ /**
+ * @return The next package that would receive the permission protected data.
+ *
+ * @hide
+ */
+ public @Nullable String getNextPackageName() {
+ if (mNext != null) {
+ return mNext.getPackageName();
+ }
+ return null;
+ }
+
+ /**
+ * @return The nexxt package's attribution tag that would receive
+ * the permission protected data.
+ *
+ * @hide
+ */
+ public @Nullable String getNextAttributionTag() {
+ if (mNext != null) {
+ return mNext.getAttributionTag();
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether this attribution source can be trusted. That is whether
+ * the app it refers to created it and provided to the attribution chain.
+ *
+ * @param context Context handle.
+ * @return Whether this is a trusted source.
+ */
+ public boolean isTrusted(@NonNull Context context) {
+ return mToken != null && context.getSystemService(PermissionManager.class)
+ .isRegisteredAttributionSource(this);
+ }
+
+ /**
+ * Permissions that should be considered revoked regardless if granted.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
+ @NonNull
+ public Set<String> getRenouncedPermissions() {
+ return CollectionUtils.emptyIfNull(mRenouncedPermissions);
+ }
+
+ @DataClass.Suppress({"setUid", "setToken"})
+ static class BaseBuilder {}
+
+
+
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/AttributionSource.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /* package-private */ AttributionSource(
+ int uid,
+ @Nullable String packageName,
+ @Nullable String attributionTag,
+ @Nullable IBinder token,
+ @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) @Nullable Set<String> renouncedPermissions,
+ @Nullable AttributionSource next) {
+ this.mUid = uid;
+ this.mPackageName = packageName;
+ this.mAttributionTag = attributionTag;
+ this.mToken = token;
+ this.mRenouncedPermissions = renouncedPermissions;
+ com.android.internal.util.AnnotationValidations.validate(
+ SystemApi.class, null, mRenouncedPermissions);
+ com.android.internal.util.AnnotationValidations.validate(
+ RequiresPermission.class, null, mRenouncedPermissions,
+ "value", android.Manifest.permission.RENOUNCE_PERMISSIONS);
+ this.mNext = next;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The UID that is accessing the permission protected data.
+ */
+ public int getUid() {
+ return mUid;
+ }
+
+ /**
+ * The package that is accessing the permission protected data.
+ */
+ public @Nullable String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * The attribution tag of the app accessing the permission protected data.
+ */
+ public @Nullable String getAttributionTag() {
+ return mAttributionTag;
+ }
+
+ /**
+ * Unique token for that source.
+ *
+ * @hide
+ */
+ public @Nullable IBinder getToken() {
+ return mToken;
+ }
+
+ /**
+ * The next app to receive the permission protected data.
+ */
+ public @Nullable AttributionSource getNext() {
+ return mNext;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(AttributionSource other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ AttributionSource that = (AttributionSource) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mUid == that.mUid
+ && Objects.equals(mPackageName, that.mPackageName)
+ && Objects.equals(mAttributionTag, that.mAttributionTag)
+ && Objects.equals(mToken, that.mToken)
+ && Objects.equals(mRenouncedPermissions, that.mRenouncedPermissions)
+ && Objects.equals(mNext, that.mNext);
+ }
+
+ @Override
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mUid;
+ _hash = 31 * _hash + Objects.hashCode(mPackageName);
+ _hash = 31 * _hash + Objects.hashCode(mAttributionTag);
+ _hash = 31 * _hash + Objects.hashCode(mToken);
+ _hash = 31 * _hash + Objects.hashCode(mRenouncedPermissions);
+ _hash = 31 * _hash + Objects.hashCode(mNext);
+ return _hash;
+ }
+
+ static Parcelling<Set<String>> sParcellingForRenouncedPermissions =
+ Parcelling.Cache.get(
+ RenouncedPermissionsParcelling.class);
+ static {
+ if (sParcellingForRenouncedPermissions == null) {
+ sParcellingForRenouncedPermissions = Parcelling.Cache.put(
+ new RenouncedPermissionsParcelling());
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mPackageName != null) flg |= 0x2;
+ if (mAttributionTag != null) flg |= 0x4;
+ if (mToken != null) flg |= 0x8;
+ if (mRenouncedPermissions != null) flg |= 0x10;
+ if (mNext != null) flg |= 0x20;
+ dest.writeByte(flg);
+ dest.writeInt(mUid);
+ if (mPackageName != null) dest.writeString(mPackageName);
+ if (mAttributionTag != null) dest.writeString(mAttributionTag);
+ if (mToken != null) dest.writeStrongBinder(mToken);
+ sParcellingForRenouncedPermissions.parcel(mRenouncedPermissions, dest, flags);
+ if (mNext != null) dest.writeTypedObject(mNext, flags);
+ }
+
+ @Override
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ /* package-private */ AttributionSource(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ int uid = in.readInt();
+ String packageName = (flg & 0x2) == 0 ? null : in.readString();
+ String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
+ IBinder token = (flg & 0x8) == 0 ? null : in.readStrongBinder();
+ Set<String> renouncedPermissions = sParcellingForRenouncedPermissions.unparcel(in);
+ AttributionSource next = (flg & 0x20) == 0 ? null : (AttributionSource) in.readTypedObject(AttributionSource.CREATOR);
+
+ this.mUid = uid;
+ this.mPackageName = packageName;
+ this.mAttributionTag = attributionTag;
+ this.mToken = token;
+ this.mRenouncedPermissions = renouncedPermissions;
+ com.android.internal.util.AnnotationValidations.validate(
+ SystemApi.class, null, mRenouncedPermissions);
+ com.android.internal.util.AnnotationValidations.validate(
+ RequiresPermission.class, null, mRenouncedPermissions,
+ "value", android.Manifest.permission.RENOUNCE_PERMISSIONS);
+ this.mNext = next;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ public static final @NonNull Parcelable.Creator<AttributionSource> CREATOR
+ = new Parcelable.Creator<AttributionSource>() {
+ @Override
+ public AttributionSource[] newArray(int size) {
+ return new AttributionSource[size];
+ }
+
+ @Override
+ public AttributionSource createFromParcel(@NonNull Parcel in) {
+ return new AttributionSource(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AttributionSource}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder extends BaseBuilder {
+
+ private int mUid;
+ private @Nullable String mPackageName;
+ private @Nullable String mAttributionTag;
+ private @Nullable IBinder mToken;
+ private @SystemApi @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) @Nullable Set<String> mRenouncedPermissions;
+ private @Nullable AttributionSource mNext;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param uid
+ * The UID that is accessing the permission protected data.
+ */
+ public Builder(
+ int uid) {
+ mUid = uid;
+ }
+
+ /**
+ * The package that is accessing the permission protected data.
+ */
+ public @NonNull Builder setPackageName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mPackageName = value;
+ return this;
+ }
+
+ /**
+ * The attribution tag of the app accessing the permission protected data.
+ */
+ public @NonNull Builder setAttributionTag(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mAttributionTag = value;
+ return this;
+ }
+
+ /**
+ * Permissions that should be considered revoked regardless if granted.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
+ public @NonNull Builder setRenouncedPermissions(@NonNull Set<String> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mRenouncedPermissions = value;
+ return this;
+ }
+
+ /**
+ * The next app to receive the permission protected data.
+ */
+ public @NonNull Builder setNext(@NonNull AttributionSource value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mNext = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AttributionSource build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mPackageName = null;
+ }
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mAttributionTag = null;
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mToken = null;
+ }
+ if ((mBuilderFieldsSet & 0x10) == 0) {
+ mRenouncedPermissions = null;
+ }
+ if ((mBuilderFieldsSet & 0x20) == 0) {
+ mNext = null;
+ }
+ AttributionSource o = new AttributionSource(
+ mUid,
+ mPackageName,
+ mAttributionTag,
+ mToken,
+ mRenouncedPermissions,
+ mNext);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x40) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 73b4f62..8284203 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -18,11 +18,6 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_ERRORED;
-import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Trace.TRACE_TAG_DATABASE;
import android.annotation.NonNull;
@@ -45,7 +40,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
@@ -57,7 +51,6 @@
import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
@@ -141,7 +134,7 @@
private boolean mNoPerms;
private boolean mSingleUser;
- private ThreadLocal<Pair<String, String>> mCallingPackage;
+ private ThreadLocal<AttributionSource> mCallingAttributionSource;
private Transport mTransport = new Transport();
@@ -231,13 +224,13 @@
}
@Override
- public Cursor query(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public Cursor query(@NonNull AttributionSource attributionSource, Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable ICancellationSignal cancellationSignal) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
// The caller has no access to the data, so return an empty cursor with
// the columns in the requested order. The caller may ask for an invalid
// column and we would not catch that but this is not a problem in practice.
@@ -253,8 +246,8 @@
// we have to execute the query as if allowed to get a cursor with the
// columns. We then use the column names to return an empty cursor.
Cursor cursor;
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
cursor = mInterface.query(
uri, projection, queryArgs,
@@ -262,7 +255,7 @@
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
}
if (cursor == null) {
return null;
@@ -272,8 +265,8 @@
return new MatrixCursor(cursor.getColumnNames(), 0);
}
Trace.traceBegin(TRACE_TAG_DATABASE, "query");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.query(
uri, projection, queryArgs,
@@ -281,7 +274,7 @@
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -314,60 +307,59 @@
}
@Override
- public Uri insert(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public Uri insert(@NonNull AttributionSource attributionSource, Uri uri,
ContentValues initialValues, Bundle extras) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ if (enforceWritePermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return rejectInsert(uri, initialValues);
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
}
}
Trace.traceBegin(TRACE_TAG_DATABASE, "insert");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return maybeAddUserId(mInterface.insert(uri, initialValues, extras), userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public int bulkInsert(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public int bulkInsert(@NonNull AttributionSource attributionSource, Uri uri,
ContentValues[] initialValues) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.bulkInsert(uri, initialValues);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg,
- @Nullable String attributionTag, String authority,
- ArrayList<ContentProviderOperation> operations)
+ public ContentProviderResult[] applyBatch(@NonNull AttributionSource attributionSource,
+ String authority, ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
validateIncomingAuthority(authority);
int numOperations = operations.size();
@@ -383,22 +375,24 @@
operation = new ContentProviderOperation(operation, uri);
operations.set(i, operation);
}
+ final AttributionSource accessAttributionSource =
+ attributionSource;
if (operation.isReadOperation()) {
- if (enforceReadPermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(accessAttributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
if (operation.isWriteOperation()) {
- if (enforceWritePermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(accessAttributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
}
Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
ContentProviderResult[] results = mInterface.applyBatch(authority,
operations);
@@ -414,111 +408,111 @@
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public int delete(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public int delete(@NonNull AttributionSource attributionSource, Uri uri,
Bundle extras) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "delete");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.delete(uri, extras);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public int update(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public int update(@NonNull AttributionSource attributionSource, Uri uri,
ContentValues values, Bundle extras) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "update");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.update(uri, values, extras);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag,
- Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken)
+ public ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSource,
+ Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, attributionTag, uri, mode, callerToken);
+ enforceFilePermission(attributionSource, uri, mode);
Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.openFile(
uri, mode, CancellationSignal.fromTransport(cancellationSignal));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag,
+ public AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionSource,
Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, attributionTag, uri, mode, null);
+ enforceFilePermission(attributionSource, uri, mode);
Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.openAssetFile(
uri, mode, CancellationSignal.fromTransport(cancellationSignal));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public Bundle call(String callingPkg, @Nullable String attributionTag, String authority,
+ public Bundle call(@NonNull AttributionSource attributionSource, String authority,
String method, @Nullable String arg, @Nullable Bundle extras) {
validateIncomingAuthority(authority);
Bundle.setDefusable(extras, true);
Trace.traceBegin(TRACE_TAG_DATABASE, "call");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.call(authority, method, arg, extras);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -539,23 +533,23 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPkg,
- @Nullable String attributionTag, Uri uri, String mimeType, Bundle opts,
- ICancellationSignal cancellationSignal) throws FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(
+ @NonNull AttributionSource attributionSource, Uri uri, String mimeType,
+ Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
Bundle.setDefusable(opts, true);
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, attributionTag, uri, "r", null);
+ enforceFilePermission(attributionSource, uri, "r");
Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.openTypedAssetFile(
uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -566,34 +560,34 @@
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri) {
+ public Uri canonicalize(@NonNull AttributionSource attributionSource, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return null;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return maybeAddUserId(mInterface.canonicalize(uri), userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public void canonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) {
final Bundle result = new Bundle();
try {
result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- canonicalize(callingPkg, attributionTag, uri));
+ canonicalize(attributionSource, uri));
} catch (Exception e) {
result.putParcelable(ContentResolver.REMOTE_CALLBACK_ERROR,
new ParcelableException(e));
@@ -602,34 +596,34 @@
}
@Override
- public Uri uncanonicalize(String callingPkg, String attributionTag, Uri uri) {
+ public Uri uncanonicalize(@NonNull AttributionSource attributionSource, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return null;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return maybeAddUserId(mInterface.uncanonicalize(uri), userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public void uncanonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public void uncanonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) {
final Bundle result = new Bundle();
try {
result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- uncanonicalize(callingPkg, attributionTag, uri));
+ uncanonicalize(attributionSource, uri));
} catch (Exception e) {
result.putParcelable(ContentResolver.REMOTE_CALLBACK_ERROR,
new ParcelableException(e));
@@ -638,92 +632,95 @@
}
@Override
- public boolean refresh(String callingPkg, String attributionTag, Uri uri, Bundle extras,
- ICancellationSignal cancellationSignal) throws RemoteException {
+ public boolean refresh(@NonNull AttributionSource attributionSource, Uri uri,
+ Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException {
uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, attributionTag, uri, null)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
return false;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "refresh");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.refresh(uri, extras,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@Override
- public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri,
+ public int checkUriPermission(@NonNull AttributionSource attributionSource, Uri uri,
int uid, int modeFlags) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission");
- final Pair<String, String> original = setCallingPackage(
- new Pair<>(callingPkg, attributionTag));
+ final AttributionSource original = setCallingAttributionSource(
+ attributionSource);
try {
return mInterface.checkUriPermission(uri, uid, modeFlags);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- setCallingPackage(original);
+ setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
- private void enforceFilePermission(String callingPkg, @Nullable String attributionTag,
- Uri uri, String mode, IBinder callerToken)
+ @PermissionChecker.PermissionResult
+ private void enforceFilePermission(@NonNull AttributionSource attributionSource,
+ Uri uri, String mode)
throws FileNotFoundException, SecurityException {
if (mode != null && mode.indexOf('w') != -1) {
- if (enforceWritePermission(callingPkg, attributionTag, uri, callerToken)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
throw new FileNotFoundException("App op not allowed");
}
} else {
- if (enforceReadPermission(callingPkg, attributionTag, uri, callerToken)
- != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(attributionSource, uri)
+ != PermissionChecker.PERMISSION_GRANTED) {
throw new FileNotFoundException("App op not allowed");
}
}
}
- private int enforceReadPermission(String callingPkg, @Nullable String attributionTag,
- Uri uri, IBinder callerToken)
+ @PermissionChecker.PermissionResult
+ private int enforceReadPermission(@NonNull AttributionSource attributionSource, Uri uri)
throws SecurityException {
- final int mode = enforceReadPermissionInner(uri, callingPkg, attributionTag,
- callerToken);
- if (mode != MODE_ALLOWED) {
- return mode;
+ final int result = enforceReadPermissionInner(uri, attributionSource);
+ if (result != PermissionChecker.PERMISSION_GRANTED) {
+ return result;
}
-
- return noteProxyOp(callingPkg, attributionTag, mReadOp);
+ // Only check the read op if it differs from the one for the permission
+ // we already checked above to avoid double attribution for every access.
+ if (mTransport.mReadOp != AppOpsManager.OP_NONE
+ && mTransport.mReadOp != AppOpsManager.permissionToOpCode(mReadPermission)) {
+ return PermissionChecker.checkOpForDataDelivery(getContext(),
+ AppOpsManager.opToPublicName(mTransport.mReadOp),
+ attributionSource, /*message*/ null);
+ }
+ return PermissionChecker.PERMISSION_GRANTED;
}
- private int enforceWritePermission(String callingPkg, String attributionTag, Uri uri,
- IBinder callerToken)
+ @PermissionChecker.PermissionResult
+ private int enforceWritePermission(@NonNull AttributionSource attributionSource, Uri uri)
throws SecurityException {
- final int mode = enforceWritePermissionInner(uri, callingPkg, attributionTag,
- callerToken);
- if (mode != MODE_ALLOWED) {
- return mode;
+ final int result = enforceWritePermissionInner(uri, attributionSource);
+ if (result != PermissionChecker.PERMISSION_GRANTED) {
+ return result;
}
-
- return noteProxyOp(callingPkg, attributionTag, mWriteOp);
- }
-
- private int noteProxyOp(String callingPkg, String attributionTag, int op) {
- if (op != AppOpsManager.OP_NONE) {
- int mode = mAppOpsManager.noteProxyOp(op, callingPkg, Binder.getCallingUid(),
- attributionTag, null);
- return mode == MODE_DEFAULT ? MODE_IGNORED : mode;
+ // Only check the write op if it differs from the one for the permission
+ // we already checked above to avoid double attribution for every access.
+ if (mTransport.mWriteOp != AppOpsManager.OP_NONE
+ && mTransport.mWriteOp != AppOpsManager.permissionToOpCode(mWritePermission)) {
+ return PermissionChecker.checkOpForDataDelivery(getContext(),
+ AppOpsManager.opToPublicName(mTransport.mWriteOp),
+ attributionSource, /*message*/ null);
}
-
- return AppOpsManager.MODE_ALLOWED;
+ return PermissionChecker.PERMISSION_GRANTED;
}
}
@@ -731,49 +728,53 @@
if (UserHandle.getUserId(uid) == context.getUserId() || mSingleUser) {
return true;
}
- return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid) == PERMISSION_GRANTED
+ return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED
|| context.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
- == PERMISSION_GRANTED;
+ == PackageManager.PERMISSION_GRANTED;
}
/**
* Verify that calling app holds both the given permission and any app-op
* associated with that permission.
*/
- private int checkPermissionAndAppOp(String permission, String callingPkg,
- @Nullable String attributionTag, IBinder callerToken) {
- if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(),
- callerToken) != PERMISSION_GRANTED) {
- return MODE_ERRORED;
+ @PermissionChecker.PermissionResult
+ private int checkPermission(String permission,
+ @NonNull AttributionSource attributionSource) {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return PermissionChecker.PERMISSION_GRANTED;
}
-
- return mTransport.noteProxyOp(callingPkg, attributionTag,
- AppOpsManager.permissionToOpCode(permission));
+ if (!attributionSource.checkCallingUid()) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(getContext(),
+ permission, -1, new AttributionSource(getContext().getAttributionSource(),
+ attributionSource), /*message*/ null);
}
/** {@hide} */
- protected int enforceReadPermissionInner(Uri uri, String callingPkg,
- @Nullable String attributionTag, IBinder callerToken) throws SecurityException {
+ @PermissionChecker.PermissionResult
+ protected int enforceReadPermissionInner(Uri uri,
+ @NonNull AttributionSource attributionSource) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
String missingPerm = null;
- int strongestMode = MODE_ALLOWED;
+ int strongestResult = PermissionChecker.PERMISSION_GRANTED;
if (UserHandle.isSameApp(uid, mMyUid)) {
- return MODE_ALLOWED;
+ return PermissionChecker.PERMISSION_GRANTED;
}
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getReadPermission();
if (componentPerm != null) {
- final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, attributionTag,
- callerToken);
- if (mode == MODE_ALLOWED) {
- return MODE_ALLOWED;
+ final int result = checkPermission(componentPerm, attributionSource);
+ if (result == PermissionChecker.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
} else {
missingPerm = componentPerm;
- strongestMode = Math.max(strongestMode, mode);
+ strongestResult = Math.max(strongestResult, result);
}
}
@@ -787,16 +788,15 @@
for (PathPermission pp : pps) {
final String pathPerm = pp.getReadPermission();
if (pathPerm != null && pp.match(path)) {
- final int mode = checkPermissionAndAppOp(pathPerm, callingPkg,
- attributionTag, callerToken);
- if (mode == MODE_ALLOWED) {
- return MODE_ALLOWED;
+ final int result = checkPermission(pathPerm, attributionSource);
+ if (result == PermissionChecker.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
} else {
// any denied <path-permission> means we lose
// default <provider> access.
allowDefaultRead = false;
missingPerm = pathPerm;
- strongestMode = Math.max(strongestMode, mode);
+ strongestResult = Math.max(strongestResult, result);
}
}
}
@@ -804,22 +804,22 @@
// if we passed <path-permission> checks above, and no default
// <provider> permission, then allow access.
- if (allowDefaultRead) return MODE_ALLOWED;
+ if (allowDefaultRead) return PermissionChecker.PERMISSION_GRANTED;
}
// last chance, check against any uri grants
final int callingUserId = UserHandle.getUserId(uid);
final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid))
? maybeAddUserId(uri, callingUserId) : uri;
- if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION,
- callerToken) == PERMISSION_GRANTED) {
- return MODE_ALLOWED;
+ if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
}
// If the worst denial we found above was ignored, then pass that
// ignored through; otherwise we assume it should be a real error below.
- if (strongestMode == MODE_IGNORED) {
- return MODE_IGNORED;
+ if (strongestResult == PermissionChecker.PERMISSION_SOFT_DENIED) {
+ return PermissionChecker.PERMISSION_SOFT_DENIED;
}
final String suffix;
@@ -836,28 +836,28 @@
}
/** {@hide} */
- protected int enforceWritePermissionInner(Uri uri, String callingPkg,
- @Nullable String attributionTag, IBinder callerToken) throws SecurityException {
+ @PermissionChecker.PermissionResult
+ protected int enforceWritePermissionInner(Uri uri,
+ @NonNull AttributionSource attributionSource) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
String missingPerm = null;
- int strongestMode = MODE_ALLOWED;
+ int strongestResult = PermissionChecker.PERMISSION_GRANTED;
if (UserHandle.isSameApp(uid, mMyUid)) {
- return MODE_ALLOWED;
+ return PermissionChecker.PERMISSION_GRANTED;
}
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getWritePermission();
if (componentPerm != null) {
- final int mode = checkPermissionAndAppOp(componentPerm, callingPkg,
- attributionTag, callerToken);
- if (mode == MODE_ALLOWED) {
- return MODE_ALLOWED;
+ final int mode = checkPermission(componentPerm, attributionSource);
+ if (mode == PermissionChecker.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
} else {
missingPerm = componentPerm;
- strongestMode = Math.max(strongestMode, mode);
+ strongestResult = Math.max(strongestResult, mode);
}
}
@@ -871,16 +871,15 @@
for (PathPermission pp : pps) {
final String pathPerm = pp.getWritePermission();
if (pathPerm != null && pp.match(path)) {
- final int mode = checkPermissionAndAppOp(pathPerm, callingPkg,
- attributionTag, callerToken);
- if (mode == MODE_ALLOWED) {
- return MODE_ALLOWED;
+ final int mode = checkPermission(pathPerm, attributionSource);
+ if (mode == PermissionChecker.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
} else {
// any denied <path-permission> means we lose
// default <provider> access.
allowDefaultWrite = false;
missingPerm = pathPerm;
- strongestMode = Math.max(strongestMode, mode);
+ strongestResult = Math.max(strongestResult, mode);
}
}
}
@@ -888,19 +887,19 @@
// if we passed <path-permission> checks above, and no default
// <provider> permission, then allow access.
- if (allowDefaultWrite) return MODE_ALLOWED;
+ if (allowDefaultWrite) return PermissionChecker.PERMISSION_GRANTED;
}
// last chance, check against any uri grants
- if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- callerToken) == PERMISSION_GRANTED) {
- return MODE_ALLOWED;
+ if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_GRANTED;
}
// If the worst denial we found above was ignored, then pass that
// ignored through; otherwise we assume it should be a real error below.
- if (strongestMode == MODE_IGNORED) {
- return MODE_IGNORED;
+ if (strongestResult == PermissionChecker.PERMISSION_SOFT_DENIED) {
+ return PermissionChecker.PERMISSION_SOFT_DENIED;
}
final String failReason = mExported
@@ -941,9 +940,10 @@
* Set the calling package/feature, returning the current value (or {@code null})
* which can be used later to restore the previous state.
*/
- private Pair<String, String> setCallingPackage(Pair<String, String> callingPackage) {
- final Pair<String, String> original = mCallingPackage.get();
- mCallingPackage.set(callingPackage);
+ private @Nullable AttributionSource setCallingAttributionSource(
+ @Nullable AttributionSource attributionSource) {
+ final AttributionSource original = mCallingAttributionSource.get();
+ mCallingAttributionSource.set(attributionSource);
onCallingPackageChanged();
return original;
}
@@ -963,13 +963,30 @@
* calling UID.
*/
public final @Nullable String getCallingPackage() {
- final Pair<String, String> pkg = mCallingPackage.get();
- if (pkg != null) {
- mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg.first);
- return pkg.first;
- }
+ final AttributionSource callingAttributionSource = getCallingAttributionSource();
+ return (callingAttributionSource != null)
+ ? callingAttributionSource.getPackageName() : null;
+ }
- return null;
+ /**
+ * Gets the attribution source of the calling app. If you want to attribute
+ * the data access to the calling app you can create an attribution context
+ * via {@link android.content.Context#createContext(ContextParams)} and passing
+ * this identity to {@link ContextParams.Builder#setNextAttributionSource(
+ * AttributionSource)}.
+ *
+ * @return The identity of the caller for permission purposes.
+ *
+ * @see ContextParams.Builder#setNextAttributionSource(AttributionSource)
+ * @see AttributionSource
+ */
+ public final @Nullable AttributionSource getCallingAttributionSource() {
+ final AttributionSource attributionSource = mCallingAttributionSource.get();
+ if (attributionSource != null) {
+ mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(),
+ attributionSource.getPackageName());
+ }
+ return attributionSource;
}
/**
@@ -983,11 +1000,10 @@
* @see #getCallingPackage
*/
public final @Nullable String getCallingAttributionTag() {
- final Pair<String, String> pkg = mCallingPackage.get();
- if (pkg != null) {
- return pkg.second;
+ final AttributionSource attributionSource = mCallingAttributionSource.get();
+ if (attributionSource != null) {
+ return attributionSource.getAttributionTag();
}
-
return null;
}
@@ -1012,11 +1028,10 @@
* @see Context#grantUriPermission(String, Uri, int)
*/
public final @Nullable String getCallingPackageUnchecked() {
- final Pair<String, String> pkg = mCallingPackage.get();
- if (pkg != null) {
- return pkg.first;
+ final AttributionSource attributionSource = mCallingAttributionSource.get();
+ if (attributionSource != null) {
+ return attributionSource.getPackageName();
}
-
return null;
}
@@ -1038,12 +1053,12 @@
/** {@hide} */
public final long binderToken;
/** {@hide} */
- public final Pair<String, String> callingPackage;
+ public final @Nullable AttributionSource callingAttributionSource;
/** {@hide} */
- public CallingIdentity(long binderToken, Pair<String, String> callingPackage) {
+ public CallingIdentity(long binderToken, @Nullable AttributionSource attributionSource) {
this.binderToken = binderToken;
- this.callingPackage = callingPackage;
+ this.callingAttributionSource = attributionSource;
}
}
@@ -1059,7 +1074,8 @@
*/
@SuppressWarnings("AndroidFrameworkBinderIdentity")
public final @NonNull CallingIdentity clearCallingIdentity() {
- return new CallingIdentity(Binder.clearCallingIdentity(), setCallingPackage(null));
+ return new CallingIdentity(Binder.clearCallingIdentity(),
+ setCallingAttributionSource(null));
}
/**
@@ -1071,7 +1087,7 @@
*/
public final void restoreCallingIdentity(@NonNull CallingIdentity identity) {
Binder.restoreCallingIdentity(identity.binderToken);
- mCallingPackage.set(identity.callingPackage);
+ mCallingAttributionSource.set(identity.callingAttributionSource);
}
/**
@@ -2374,7 +2390,7 @@
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
- mCallingPackage = new ThreadLocal<>();
+ mCallingAttributionSource = new ThreadLocal<>();
/*
* Only allow it to be set once, so after the content service gives
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 5af7861..518e753 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -79,7 +79,8 @@
private final IContentProvider mContentProvider;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mPackageName;
- private final @Nullable String mAttributionTag;
+ private final @NonNull AttributionSource mAttributionSource;
+
private final String mAuthority;
private final boolean mStable;
@@ -103,7 +104,7 @@
mContentResolver = contentResolver;
mContentProvider = contentProvider;
mPackageName = contentResolver.mPackageName;
- mAttributionTag = contentResolver.mAttributionTag;
+ mAttributionSource = contentResolver.getAttributionSource();
mAuthority = authority;
mStable = stable;
@@ -193,7 +194,7 @@
cancellationSignal.setRemote(remoteCancellationSignal);
}
final Cursor cursor = mContentProvider.query(
- mPackageName, mAttributionTag, uri, projection, queryArgs,
+ mAttributionSource, uri, projection, queryArgs,
remoteCancellationSignal);
if (cursor == null) {
return null;
@@ -254,7 +255,7 @@
beforeRemote();
try {
- return mContentProvider.canonicalize(mPackageName, mAttributionTag, url);
+ return mContentProvider.canonicalize(mAttributionSource, url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -272,7 +273,7 @@
beforeRemote();
try {
- return mContentProvider.uncanonicalize(mPackageName, mAttributionTag, url);
+ return mContentProvider.uncanonicalize(mAttributionSource, url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -297,7 +298,7 @@
remoteCancellationSignal = mContentProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return mContentProvider.refresh(mPackageName, mAttributionTag, url, extras,
+ return mContentProvider.refresh(mAttributionSource, url, extras,
remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -317,7 +318,7 @@
beforeRemote();
try {
- return mContentProvider.checkUriPermission(mPackageName, mAttributionTag, uri, uid,
+ return mContentProvider.checkUriPermission(mAttributionSource, uri, uid,
modeFlags);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -343,7 +344,7 @@
beforeRemote();
try {
- return mContentProvider.insert(mPackageName, mAttributionTag, url, initialValues,
+ return mContentProvider.insert(mAttributionSource, url, initialValues,
extras);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -364,7 +365,7 @@
beforeRemote();
try {
- return mContentProvider.bulkInsert(mPackageName, mAttributionTag, url, initialValues);
+ return mContentProvider.bulkInsert(mAttributionSource, url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -388,7 +389,7 @@
beforeRemote();
try {
- return mContentProvider.delete(mPackageName, mAttributionTag, url, extras);
+ return mContentProvider.delete(mAttributionSource, url, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -413,7 +414,7 @@
beforeRemote();
try {
- return mContentProvider.update(mPackageName, mAttributionTag, url, values, extras);
+ return mContentProvider.update(mAttributionSource, url, values, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -457,8 +458,7 @@
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openFile(mPackageName, mAttributionTag, url, mode,
- remoteSignal, null);
+ return mContentProvider.openFile(mAttributionSource, url, mode, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -502,7 +502,7 @@
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openAssetFile(mPackageName, mAttributionTag, url, mode,
+ return mContentProvider.openAssetFile(mAttributionSource, url, mode,
remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -544,7 +544,7 @@
signal.setRemote(remoteSignal);
}
return mContentProvider.openTypedAssetFile(
- mPackageName, mAttributionTag, uri, mimeTypeFilter, opts, remoteSignal);
+ mAttributionSource, uri, mimeTypeFilter, opts, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -571,7 +571,7 @@
beforeRemote();
try {
- return mContentProvider.applyBatch(mPackageName, mAttributionTag, authority,
+ return mContentProvider.applyBatch(mAttributionSource, authority,
operations);
} catch (DeadObjectException e) {
if (!mStable) {
@@ -598,7 +598,7 @@
beforeRemote();
try {
- return mContentProvider.call(mPackageName, mAttributionTag, authority, method, arg,
+ return mContentProvider.call(mAttributionSource, authority, method, arg,
extras);
} catch (DeadObjectException e) {
if (!mStable) {
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 7d121d5..47c96699 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -16,6 +16,7 @@
package android.content;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetFileDescriptor;
@@ -83,8 +84,8 @@
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String callingFeatureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
// String[] projection
@@ -103,7 +104,7 @@
ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- Cursor cursor = query(callingPkg, callingFeatureId, url, projection, queryArgs,
+ Cursor cursor = query(attributionSource, url, projection, queryArgs,
cancellationSignal);
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = null;
@@ -158,13 +159,13 @@
case INSERT_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
Bundle extras = data.readBundle();
- Uri out = insert(callingPkg, featureId, url, values, extras);
+ Uri out = insert(attributionSource, url, values, extras);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -173,12 +174,12 @@
case BULK_INSERT_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues[] values = data.createTypedArray(ContentValues.CREATOR);
- int count = bulkInsert(callingPkg, featureId, url, values);
+ int count = bulkInsert(attributionSource, url, values);
reply.writeNoException();
reply.writeInt(count);
return true;
@@ -187,8 +188,8 @@
case APPLY_BATCH_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
String authority = data.readString();
final int numOperations = data.readInt();
final ArrayList<ContentProviderOperation> operations =
@@ -196,7 +197,7 @@
for (int i = 0; i < numOperations; i++) {
operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
}
- final ContentProviderResult[] results = applyBatch(callingPkg, featureId,
+ final ContentProviderResult[] results = applyBatch(attributionSource,
authority, operations);
reply.writeNoException();
reply.writeTypedArray(results, 0);
@@ -206,12 +207,12 @@
case DELETE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
Bundle extras = data.readBundle();
- int count = delete(callingPkg, featureId, url, extras);
+ int count = delete(attributionSource, url, extras);
reply.writeNoException();
reply.writeInt(count);
@@ -221,13 +222,13 @@
case UPDATE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
Bundle extras = data.readBundle();
- int count = update(callingPkg, featureId, url, values, extras);
+ int count = update(attributionSource, url, values, extras);
reply.writeNoException();
reply.writeInt(count);
@@ -237,16 +238,15 @@
case OPEN_FILE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- IBinder callerToken = data.readStrongBinder();
ParcelFileDescriptor fd;
- fd = openFile(callingPkg, featureId, url, mode, signal, callerToken);
+ fd = openFile(attributionSource, url, mode, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -261,15 +261,15 @@
case OPEN_ASSET_FILE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openAssetFile(callingPkg, featureId, url, mode, signal);
+ fd = openAssetFile(attributionSource, url, mode, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -285,14 +285,14 @@
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
String authority = data.readString();
String method = data.readString();
String stringArg = data.readString();
Bundle extras = data.readBundle();
- Bundle responseBundle = call(callingPkg, featureId, authority, method,
+ Bundle responseBundle = call(attributionSource, authority, method,
stringArg, extras);
reply.writeNoException();
@@ -315,8 +315,8 @@
case OPEN_TYPED_ASSET_FILE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
String mimeType = data.readString();
Bundle opts = data.readBundle();
@@ -324,7 +324,7 @@
data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openTypedAssetFile(callingPkg, featureId, url, mimeType, opts, signal);
+ fd = openTypedAssetFile(attributionSource, url, mimeType, opts, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -349,11 +349,11 @@
case CANONICALIZE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
- Uri out = canonicalize(callingPkg, featureId, url);
+ Uri out = canonicalize(attributionSource, url);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -361,22 +361,22 @@
case CANONICALIZE_ASYNC_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri uri = Uri.CREATOR.createFromParcel(data);
RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
- canonicalizeAsync(callingPkg, featureId, uri, callback);
+ canonicalizeAsync(attributionSource, uri, callback);
return true;
}
case UNCANONICALIZE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
- Uri out = uncanonicalize(callingPkg, featureId, url);
+ Uri out = uncanonicalize(attributionSource, url);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -384,24 +384,24 @@
case UNCANONICALIZE_ASYNC_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri uri = Uri.CREATOR.createFromParcel(data);
RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
- uncanonicalizeAsync(callingPkg, featureId, uri, callback);
+ uncanonicalizeAsync(attributionSource, uri, callback);
return true;
}
case REFRESH_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
Bundle extras = data.readBundle();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- boolean out = refresh(callingPkg, featureId, url, extras, signal);
+ boolean out = refresh(attributionSource, url, extras, signal);
reply.writeNoException();
reply.writeInt(out ? 0 : -1);
return true;
@@ -409,13 +409,13 @@
case CHECK_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
- String callingPkg = data.readString();
- String featureId = data.readString();
+ AttributionSource attributionSource = AttributionSource.CREATOR
+ .createFromParcel(data);
Uri uri = Uri.CREATOR.createFromParcel(data);
int uid = data.readInt();
int modeFlags = data.readInt();
- int out = checkUriPermission(callingPkg, featureId, uri, uid, modeFlags);
+ int out = checkUriPermission(attributionSource, uri, uid, modeFlags);
reply.writeNoException();
reply.writeInt(out);
return true;
@@ -451,7 +451,7 @@
}
@Override
- public Cursor query(String callingPkg, @Nullable String featureId, Uri url,
+ public Cursor query(@NonNull AttributionSource attributionSource, Uri url,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable ICancellationSignal cancellationSignal)
throws RemoteException {
@@ -461,8 +461,7 @@
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
int length = 0;
if (projection != null) {
@@ -540,7 +539,7 @@
}
@Override
- public Uri insert(String callingPkg, @Nullable String featureId, Uri url,
+ public Uri insert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues values, Bundle extras) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -548,8 +547,7 @@
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
data.writeBundle(extras);
@@ -566,15 +564,14 @@
}
@Override
- public int bulkInsert(String callingPkg, @Nullable String featureId, Uri url,
+ public int bulkInsert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues[] values) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeTypedArray(values, 0);
@@ -590,15 +587,14 @@
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+ public ContentProviderResult[] applyBatch(@NonNull AttributionSource attributionSource,
String authority, ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
data.writeString(authority);
data.writeInt(operations.size());
for (ContentProviderOperation operation : operations) {
@@ -617,15 +613,14 @@
}
@Override
- public int delete(String callingPkg, @Nullable String featureId, Uri url, Bundle extras)
+ public int delete(@NonNull AttributionSource attributionSource, Uri url, Bundle extras)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeBundle(extras);
@@ -641,15 +636,14 @@
}
@Override
- public int update(String callingPkg, @Nullable String featureId, Uri url,
+ public int update(@NonNull AttributionSource attributionSource, Uri url,
ContentValues values, Bundle extras) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
data.writeBundle(extras);
@@ -666,20 +660,18 @@
}
@Override
- public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url,
- String mode, ICancellationSignal signal, IBinder token)
+ public ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSource, Uri url,
+ String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeString(mode);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
- data.writeStrongBinder(token);
mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0);
@@ -695,7 +687,7 @@
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+ public AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionSource,
Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
@@ -703,8 +695,7 @@
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeString(mode);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
@@ -723,15 +714,14 @@
}
@Override
- public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+ public Bundle call(@NonNull AttributionSource attributionSource, String authority,
String method, String request, Bundle extras) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
data.writeString(authority);
data.writeString(method);
data.writeString(request);
@@ -771,7 +761,7 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId,
+ public AssetFileDescriptor openTypedAssetFile(@NonNull AttributionSource attributionSource,
Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
@@ -779,8 +769,7 @@
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeString(mimeType);
data.writeBundle(opts);
@@ -820,15 +809,14 @@
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri url)
+ public Uri canonicalize(@NonNull AttributionSource attributionSource, Uri url)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
mRemote.transact(IContentProvider.CANONICALIZE_TRANSACTION, data, reply, 0);
@@ -843,14 +831,13 @@
}
@Override
- /* oneway */ public void canonicalizeAsync(String callingPkg, @Nullable String featureId,
+ /* oneway */ public void canonicalizeAsync(@NonNull AttributionSource attributionSource,
Uri uri, RemoteCallback callback) throws RemoteException {
Parcel data = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
uri.writeToParcel(data, 0);
callback.writeToParcel(data, 0);
@@ -862,15 +849,14 @@
}
@Override
- public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url)
+ public Uri uncanonicalize(@NonNull AttributionSource attributionSource, Uri url)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
mRemote.transact(IContentProvider.UNCANONICALIZE_TRANSACTION, data, reply, 0);
@@ -885,14 +871,13 @@
}
@Override
- /* oneway */ public void uncanonicalizeAsync(String callingPkg, @Nullable String featureId,
+ /* oneway */ public void uncanonicalizeAsync(@NonNull AttributionSource attributionSource,
Uri uri, RemoteCallback callback) throws RemoteException {
Parcel data = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
uri.writeToParcel(data, 0);
callback.writeToParcel(data, 0);
@@ -904,15 +889,14 @@
}
@Override
- public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle extras,
+ public boolean refresh(@NonNull AttributionSource attributionSource, Uri url, Bundle extras,
ICancellationSignal signal) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeBundle(extras);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
@@ -929,15 +913,14 @@
}
@Override
- public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri url, int uid,
+ public int checkUriPermission(@NonNull AttributionSource attributionSource, Uri url, int uid,
int modeFlags) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeString(callingPkg);
- data.writeString(featureId);
+ attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
data.writeInt(uid);
data.writeInt(modeFlags);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 8ea417f..14b2a65 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -816,7 +816,6 @@
public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) {
mContext = context != null ? context : ActivityThread.currentApplication();
mPackageName = mContext.getOpPackageName();
- mAttributionTag = mContext.getAttributionTag();
mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
mWrapped = wrapped;
}
@@ -1217,7 +1216,7 @@
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
- qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection,
+ qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
@@ -1228,7 +1227,7 @@
if (stableProvider == null) {
return null;
}
- qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,
+ qCursor = stableProvider.query(mContext.getAttributionSource(), uri, projection,
queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
@@ -1320,7 +1319,7 @@
try {
final UriResultListener resultListener = new UriResultListener();
- provider.canonicalizeAsync(mPackageName, mAttributionTag, url,
+ provider.canonicalizeAsync(mContext.getAttributionSource(), url,
new RemoteCallback(resultListener));
resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
if (resultListener.exception != null) {
@@ -1371,7 +1370,7 @@
try {
final UriResultListener resultListener = new UriResultListener();
- provider.uncanonicalizeAsync(mPackageName, mAttributionTag, url,
+ provider.uncanonicalizeAsync(mContext.getAttributionSource(), url,
new RemoteCallback(resultListener));
resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
if (resultListener.exception != null) {
@@ -1429,7 +1428,7 @@
remoteCancellationSignal = provider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return provider.refresh(mPackageName, mAttributionTag, url, extras,
+ return provider.refresh(mContext.getAttributionSource(), url, extras,
remoteCancellationSignal);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
@@ -1858,7 +1857,7 @@
try {
fd = unstableProvider.openAssetFile(
- mPackageName, mAttributionTag, uri, mode,
+ mContext.getAttributionSource(), uri, mode,
remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
@@ -1873,8 +1872,8 @@
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
- fd = stableProvider.openAssetFile(
- mPackageName, mAttributionTag, uri, mode, remoteCancellationSignal);
+ fd = stableProvider.openAssetFile(mContext.getAttributionSource(),
+ uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -2025,7 +2024,7 @@
try {
fd = unstableProvider.openTypedAssetFile(
- mPackageName, mAttributionTag, uri, mimeType, opts,
+ mContext.getAttributionSource(), uri, mimeType, opts,
remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
@@ -2041,7 +2040,7 @@
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openTypedAssetFile(
- mPackageName, mAttributionTag, uri, mimeType, opts,
+ mContext.getAttributionSource(), uri, mimeType, opts,
remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
@@ -2190,7 +2189,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- Uri createdRow = provider.insert(mPackageName, mAttributionTag, url, values, extras);
+ Uri createdRow = provider.insert(mContext.getAttributionSource(), url, values, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
@@ -2271,7 +2270,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsCreated = provider.bulkInsert(mPackageName, mAttributionTag, url, values);
+ int rowsCreated = provider.bulkInsert(mContext.getAttributionSource(), url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
return rowsCreated;
@@ -2330,7 +2329,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsDeleted = provider.delete(mPackageName, mAttributionTag, url, extras);
+ int rowsDeleted = provider.delete(mContext.getAttributionSource(), url, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "delete", null);
return rowsDeleted;
@@ -2397,7 +2396,8 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsUpdated = provider.update(mPackageName, mAttributionTag, uri, values, extras);
+ int rowsUpdated = provider.update(mContext.getAttributionSource(),
+ uri, values, extras);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, uri, "update", null);
return rowsUpdated;
@@ -2446,8 +2446,8 @@
throw new IllegalArgumentException("Unknown authority " + authority);
}
try {
- final Bundle res = provider.call(mPackageName, mAttributionTag, authority, method, arg,
- extras);
+ final Bundle res = provider.call(mContext.getAttributionSource(),
+ authority, method, arg, extras);
Bundle.setDefusable(res, true);
return res;
} catch (RemoteException e) {
@@ -3866,12 +3866,17 @@
/** @hide */
@UnsupportedAppUsage
public String getPackageName() {
- return mPackageName;
+ return mContext.getOpPackageName();
}
/** @hide */
public @Nullable String getAttributionTag() {
- return mAttributionTag;
+ return mContext.getAttributionTag();
+ }
+
+ /** @hide */
+ public @NonNull AttributionSource getAttributionSource() {
+ return mContext.getAttributionSource();
}
@UnsupportedAppUsage
@@ -3881,7 +3886,6 @@
@UnsupportedAppUsage
final String mPackageName;
- final @Nullable String mAttributionTag;
final int mTargetSdkVersion;
final ContentInterface mWrapped;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 45a8ffd..8531d34 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -396,7 +396,7 @@
*
* @hide
*/
- public static final int BIND_ALLOW_NETWORK_ACCESS = 0x00020000;
+ public static final int BIND_BYPASS_POWER_NETWORK_RESTRICTIONS = 0x00020000;
/**
* Flag for {@link #bindService}: allow background foreground service starts from the bound
@@ -889,6 +889,15 @@
return null;
}
+ /**
+ * @return The identity of this context for permission purposes.
+ *
+ * @see AttributionSource
+ */
+ public @NonNull AttributionSource getAttributionSource() {
+ return null;
+ }
+
// TODO moltmann: Remove
/**
* @removed
@@ -6465,8 +6474,10 @@
* @removed
*/
@Deprecated
- public @NonNull Context createFeatureContext(@Nullable String featureId) {
- return createAttributionContext(featureId);
+ public @NonNull Context createFeatureContext(@Nullable String attributionTag) {
+ return createContext(new ContextParams.Builder()
+ .setAttributionTag(attributionTag)
+ .build());
}
/**
diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java
index fad905b..2b2db8f 100644
--- a/core/java/android/content/ContextParams.java
+++ b/core/java/android/content/ContextParams.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import java.util.Collections;
@@ -38,36 +37,26 @@
* is an arbitrary string your app specifies for the purposes of tracking permission
* accesses from a given portion of your app; against another package and optionally
* its attribution tag if you are accessing the data on behalf of another app and
- * you will be passing that data to this app. Both attributions are not mutually
- * exclusive.
- *
- * <p>For example if you have a feature "foo" in your app which accesses
- * permissions on behalf of app "foo.bar.baz" with feature "bar" you need to
- * create a context like this:
- *
- * <pre class="prettyprint">
- * context.createContext(new ContextParams.Builder()
- * .setAttributionTag("foo")
- * .setReceiverPackage("foo.bar.baz", "bar")
- * .build())
- * </pre>
+ * you will be passing that data to this app, recursively. Both attributions are
+ * not mutually exclusive.
*
* @see Context#createContext(ContextParams)
+ * @see AttributionSource
*/
public final class ContextParams {
- private final String mAttributionTag;
- private final String mReceiverPackage;
- private final String mReceiverAttributionTag;
- private final Set<String> mRenouncedPermissions;
+ private final @Nullable String mAttributionTag;
+ private final @Nullable AttributionSource mNext;
+ private final @NonNull Set<String> mRenouncedPermissions;
/** {@hide} */
public static final ContextParams EMPTY = new ContextParams.Builder().build();
- private ContextParams(@NonNull ContextParams.Builder builder) {
- mAttributionTag = builder.mAttributionTag;
- mReceiverPackage = builder.mReceiverPackage;
- mReceiverAttributionTag = builder.mReceiverAttributionTag;
- mRenouncedPermissions = builder.mRenouncedPermissions;
+ private ContextParams(@Nullable String attributionTag,
+ @Nullable AttributionSource next,
+ @NonNull Set<String> renouncedPermissions) {
+ mAttributionTag = attributionTag;
+ mNext = next;
+ mRenouncedPermissions = renouncedPermissions;
}
/**
@@ -79,45 +68,35 @@
}
/**
- * @return The receiving package.
- */
- @Nullable
- public String getReceiverPackage() {
- return mReceiverPackage;
- }
-
- /**
- * @return The receiving package's attribution tag.
- */
- @Nullable
- public String getReceiverAttributionTag() {
- return mReceiverAttributionTag;
- }
-
- /**
* @return The set of permissions to treat as renounced.
* @hide
*/
@SystemApi
- @SuppressLint("NullableCollection")
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
- public @Nullable Set<String> getRenouncedPermissions() {
+ public @NonNull Set<String> getRenouncedPermissions() {
return mRenouncedPermissions;
}
/** @hide */
public boolean isRenouncedPermission(@NonNull String permission) {
- return mRenouncedPermissions != null && mRenouncedPermissions.contains(permission);
+ return mRenouncedPermissions.contains(permission);
+ }
+
+ /**
+ * @return The receiving attribution source.
+ */
+ @Nullable
+ public AttributionSource getNextAttributionSource() {
+ return mNext;
}
/**
* Builder for creating a {@link ContextParams}.
*/
public static final class Builder {
- private String mAttributionTag;
- private String mReceiverPackage;
- private String mReceiverAttributionTag;
- private Set<String> mRenouncedPermissions;
+ private @Nullable String mAttributionTag;
+ private @NonNull Set<String> mRenouncedPermissions = Collections.emptySet();
+ private @Nullable AttributionSource mNext;
/**
* Create a new builder.
@@ -145,9 +124,8 @@
public Builder(@NonNull ContextParams params) {
Objects.requireNonNull(params);
mAttributionTag = params.mAttributionTag;
- mReceiverPackage = params.mReceiverPackage;
- mReceiverAttributionTag = params.mReceiverAttributionTag;
mRenouncedPermissions = params.mRenouncedPermissions;
+ mNext = params.mNext;
}
/**
@@ -163,18 +141,16 @@
}
/**
- * Sets the package and its optional attribution tag that would be receiving
- * the permission protected data.
+ * Sets the attribution source for the app on whose behalf you are doing the work.
*
- * @param packageName The package name receiving the permission protected data.
- * @param attributionTag An attribution tag of the receiving package.
+ * @param next The permission identity of the receiving app.
* @return This builder.
+ *
+ * @see AttributionSource
*/
@NonNull
- public Builder setReceiverPackage(@Nullable String packageName,
- @Nullable String attributionTag) {
- mReceiverPackage = packageName;
- mReceiverAttributionTag = attributionTag;
+ public Builder setNextAttributionSource(@NonNull AttributionSource next) {
+ mNext = Objects.requireNonNull(next);
return this;
}
@@ -194,19 +170,16 @@
* permissions are supported by this mechanism.
*
* @param renouncedPermissions The set of permissions to treat as
- * renounced.
+ * renounced, which is as if not granted.
* @return This builder.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
public @NonNull Builder setRenouncedPermissions(
- @Nullable Set<String> renouncedPermissions) {
- if (renouncedPermissions != null) {
- mRenouncedPermissions = Collections.unmodifiableSet(renouncedPermissions);
- } else {
- mRenouncedPermissions = null;
- }
+ @NonNull Set<String> renouncedPermissions) {
+ mRenouncedPermissions = Collections.unmodifiableSet(
+ Objects.requireNonNull(renouncedPermissions));
return this;
}
@@ -217,7 +190,8 @@
*/
@NonNull
public ContextParams build() {
- return new ContextParams(this);
+ return new ContextParams(mAttributionTag, mNext,
+ mRenouncedPermissions);
}
}
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 609f417..de0d65f 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1060,6 +1060,12 @@
return mBase.createAttributionContext(attributionTag);
}
+ @NonNull
+ @Override
+ public AttributionSource getAttributionSource() {
+ return mBase.getAttributionSource();
+ }
+
@Override
public boolean isRestricted() {
return mBase.isRestricted();
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 9210b13..e0315a3 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -16,11 +16,13 @@
package android.content;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
@@ -38,11 +40,11 @@
* @hide
*/
public interface IContentProvider extends IInterface {
- public Cursor query(String callingPkg, @Nullable String attributionTag, Uri url,
+ Cursor query(@NonNull AttributionSource attributionSource, Uri url,
@Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
throws RemoteException;
- public String getType(Uri url) throws RemoteException;
+ String getType(Uri url) throws RemoteException;
/**
* A oneway version of getType. The functionality is exactly the same, except that the
@@ -55,54 +57,56 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#insert(android.net.Uri, android.content.ContentValues)} "
+ "instead")
- public default Uri insert(String callingPkg, Uri url, ContentValues initialValues)
+ default Uri insert(String callingPkg, Uri url, ContentValues initialValues)
throws RemoteException {
- return insert(callingPkg, null, url, initialValues, null);
+ return insert(new AttributionSource(Binder.getCallingUid(), callingPkg, null),
+ url, initialValues, null);
}
- public Uri insert(String callingPkg, String attributionTag, Uri url,
+ Uri insert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues initialValues, Bundle extras) throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#bulkInsert(android.net.Uri, android.content.ContentValues[])"
+ "} instead")
- public default int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
+ default int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
throws RemoteException {
- return bulkInsert(callingPkg, null, url, initialValues);
+ return bulkInsert(new AttributionSource(Binder.getCallingUid(), callingPkg, null),
+ url, initialValues);
}
- public int bulkInsert(String callingPkg, String attributionTag, Uri url,
+ int bulkInsert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues[] initialValues) throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#delete(android.net.Uri, java.lang.String, java.lang"
+ ".String[])} instead")
- public default int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
+ default int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
throws RemoteException {
- return delete(callingPkg, null, url,
- ContentResolver.createSqlQueryBundle(selection, selectionArgs));
+ return delete(new AttributionSource(Binder.getCallingUid(), callingPkg, null),
+ url, ContentResolver.createSqlQueryBundle(selection, selectionArgs));
}
- public int delete(String callingPkg, String attributionTag, Uri url, Bundle extras)
+ int delete(@NonNull AttributionSource attributionSource, Uri url, Bundle extras)
throws RemoteException;
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#update(android.net.Uri, android.content.ContentValues, java"
+ ".lang.String, java.lang.String[])} instead")
- public default int update(String callingPkg, Uri url, ContentValues values, String selection,
+ default int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException {
- return update(callingPkg, null, url, values,
- ContentResolver.createSqlQueryBundle(selection, selectionArgs));
+ return update(new AttributionSource(Binder.getCallingUid(), callingPkg, null),
+ url, values, ContentResolver.createSqlQueryBundle(selection, selectionArgs));
}
- public int update(String callingPkg, String attributionTag, Uri url, ContentValues values,
+ int update(@NonNull AttributionSource attributionSource, Uri url, ContentValues values,
Bundle extras) throws RemoteException;
- public ParcelFileDescriptor openFile(String callingPkg, @Nullable String attributionTag,
- Uri url, String mode, ICancellationSignal signal, IBinder callerToken)
- throws RemoteException, FileNotFoundException;
-
- public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String attributionTag,
+ ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSource,
Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
- public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String attributionTag,
+ AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mode, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException;
+
+ ContentProviderResult[] applyBatch(@NonNull AttributionSource attributionSource,
String authority, ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
@@ -112,18 +116,19 @@
+ "instead")
public default Bundle call(String callingPkg, String method,
@Nullable String arg, @Nullable Bundle extras) throws RemoteException {
- return call(callingPkg, null, "unknown", method, arg, extras);
+ return call(new AttributionSource(Binder.getCallingUid(), callingPkg, null),
+ "unknown", method, arg, extras);
}
- public Bundle call(String callingPkg, @Nullable String attributionTag, String authority,
+ Bundle call(@NonNull AttributionSource attributionSource, String authority,
String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
- public int checkUriPermission(String callingPkg, @Nullable String attributionTag, Uri uri,
+ int checkUriPermission(@NonNull AttributionSource attributionSource, Uri uri,
int uid, int modeFlags) throws RemoteException;
- public ICancellationSignal createCancellationSignal() throws RemoteException;
+ ICancellationSignal createCancellationSignal() throws RemoteException;
- public Uri canonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
+ Uri canonicalize(@NonNull AttributionSource attributionSource, Uri uri)
throws RemoteException;
/**
@@ -131,10 +136,10 @@
* call returns immediately, and the resulting type is returned when available via
* a binder callback.
*/
- void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+ void canonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) throws RemoteException;
- public Uri uncanonicalize(String callingPkg, @Nullable String attributionTag, Uri uri)
+ Uri uncanonicalize(@NonNull AttributionSource attributionSource, Uri uri)
throws RemoteException;
/**
@@ -142,18 +147,17 @@
* call returns immediately, and the resulting type is returned when available via
* a binder callback.
*/
- void uncanonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
+ void uncanonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) throws RemoteException;
- public boolean refresh(String callingPkg, @Nullable String attributionTag, Uri url,
+ public boolean refresh(@NonNull AttributionSource attributionSource, Uri url,
@Nullable Bundle extras, ICancellationSignal cancellationSignal) throws RemoteException;
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
- public AssetFileDescriptor openTypedAssetFile(String callingPkg,
- @Nullable String attributionTag, Uri url, String mimeType, Bundle opts,
- ICancellationSignal signal)
+ public AssetFileDescriptor openTypedAssetFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
/* IPC constants */
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 159db92..08eac5a 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -27,6 +27,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* This class provides permission check APIs that verify both the
@@ -68,8 +70,14 @@
* @hide
*/
public final class PermissionChecker {
+ private static final String PLATFORM_PACKAGE_NAME = "android";
+
/** The permission is granted. */
- public static final int PERMISSION_GRANTED = PackageManager.PERMISSION_GRANTED;
+ public static final int PERMISSION_GRANTED = AppOpsManager.MODE_ALLOWED;
+
+ /** Only for runtime permissions, its returned when the runtime permission
+ * is granted, but the corresponding app op is denied. */
+ public static final int PERMISSION_SOFT_DENIED = AppOpsManager.MODE_IGNORED;
/** Returned when:
* <ul>
@@ -79,15 +87,14 @@
* </ul>
*
*/
- public static final int PERMISSION_HARD_DENIED = PackageManager.PERMISSION_DENIED;
-
- /** Only for runtime permissions, its returned when the runtime permission
- * is granted, but the corresponding app op is denied. */
- public static final int PERMISSION_SOFT_DENIED = PackageManager.PERMISSION_DENIED - 1;
+ public static final int PERMISSION_HARD_DENIED = AppOpsManager.MODE_ERRORED;
/** Constant when the PID for which we check permissions is unknown. */
public static final int PID_UNKNOWN = -1;
+ private static final ConcurrentHashMap<String, PermissionInfo> sPlatformPermissions
+ = new ConcurrentHashMap<>();
+
/** @hide */
@IntDef({PERMISSION_GRANTED,
PERMISSION_SOFT_DENIED,
@@ -131,6 +138,50 @@
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
* @param message A message describing the reason the permission was checked
+ * @param startDataDelivery Whether this is the start of data delivery.
+ *
+ * @see #checkPermissionForPreflight(Context, String, int, int, String)
+ */
+ @PermissionResult
+ public static int checkPermissionForDataDelivery(@NonNull Context context,
+ @NonNull String permission, int pid, int uid, @Nullable String packageName,
+ @Nullable String attributionTag, @Nullable String message, boolean startDataDelivery) {
+ return checkPermissionForDataDelivery(context, permission, pid, new AttributionSource(uid,
+ packageName, attributionTag), message, startDataDelivery);
+ }
+
+ /**
+ * Checks whether a given package in a UID and PID has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * <p>For more details how to determine the {@code packageName}, {@code attributionTag}, and
+ * {@code message}, please check the description in
+ * {@link AppOpsManager#noteOp(String, int, String, String, String)}
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+ * is not known.
+ * @param uid The uid for which to check.
+ * @param packageName The package name for which to check. If null the
+ * the first package for the calling UID will be used.
+ * @param attributionTag attribution tag
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ * @param message A message describing the reason the permission was checked
*
* @see #checkPermissionForPreflight(Context, String, int, int, String)
*/
@@ -138,8 +189,303 @@
public static int checkPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, int pid, int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable String message) {
- return checkPermissionCommon(context, permission, pid, uid, packageName, attributionTag,
- message, true /*forDataDelivery*/);
+ return checkPermissionForDataDelivery(context, permission, pid, uid,
+ packageName, attributionTag, message, false /*startDataDelivery*/);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed. Call this method if you are the datasource which would not blame you for
+ * access to the data since you are the data.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+ * is not known.
+ * @param attributionSource the permission identity
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkPermissionForPreflight(Context, String, AttributionSource)
+ */
+ @PermissionResult
+ public static int checkPermissionForDataDeliveryFromDataSource(@NonNull Context context,
+ @NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return checkPermissionForDataDeliveryCommon(context, permission, pid, attributionSource,
+ message, false /*startDataDelivery*/, /*fromDatasource*/ true);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String, AttributionSource)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+ * is not known.
+ * @param attributionSource the permission identity
+ * @param message A message describing the reason the permission was checked
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkPermissionForPreflight(Context, String, AttributionSource)
+ */
+ @PermissionResult
+ public static int checkPermissionForDataDelivery(@NonNull Context context,
+ @NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return checkPermissionForDataDelivery(context, permission, pid, attributionSource,
+ message, false /*startDataDelivery*/);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a data listener it should have the required
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String,
+ * AttributionSource)}
+ * to determine if the app has or may have permission and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+ * is not known.
+ * @param attributionSource The identity for which to check the permission.
+ * @param message A message describing the reason the permission was checked
+ * @param startDataDelivery Whether this is the start of data delivery.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkPermissionForPreflight(Context, String, AttributionSource)
+ */
+ @PermissionResult
+ public static int checkPermissionForDataDelivery(@NonNull Context context,
+ @NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean startDataDelivery) {
+ return checkPermissionForDataDeliveryCommon(context, permission, pid, attributionSource,
+ message, startDataDelivery, /*fromDatasource*/ false);
+ }
+
+ private static int checkPermissionForDataDeliveryCommon(@NonNull Context context,
+ @NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean startDataDelivery, boolean fromDatasource) {
+ // If the check failed in the middle of the chain, finish any started op.
+ final int result = checkPermissionCommon(context, permission, attributionSource,
+ message, true /*forDataDelivery*/, startDataDelivery, fromDatasource);
+ if (startDataDelivery && result != PERMISSION_GRANTED) {
+ finishDataDelivery(context, AppOpsManager.permissionToOp(permission),
+ attributionSource);
+ }
+ return result;
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed. The app ops area also marked as started. This is useful for long running
+ * permissions like camera.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a data listener it should have the required
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String,
+ * AttributionSource)}
+ * to determine if the app has or may have permission and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param attributionSource The identity for which to check the permission.
+ * @param message A message describing the reason the permission was checked
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkPermissionForPreflight(Context, String, AttributionSource)
+ */
+ @PermissionResult
+ public static int checkPermissionAndStartDataDelivery(@NonNull Context context,
+ @NonNull String permission, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return checkPermissionCommon(context, permission, attributionSource,
+ message, true /*forDataDelivery*/, /*startDataDelivery*/ true,
+ /*fromDatasource*/ false);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link
+ * AttributionSource} has a given app op allowed and marks the op as started.
+ *
+ * <strong>NOTE:</strong> Use this method only for app op checks at the
+ * point where you will deliver the protected data to clients.
+ *
+ * <p>For example, if an app registers a data listener it should have the data
+ * op but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkOpForPreflight(Context, String, AttributionSource, String)}
+ * to determine if the app has or may have op access and this check will not
+ * leave a trace that op protected data was delivered. When you are about to
+ * deliver the data to a registered listener you should use this method which
+ * will evaluate the op access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param opName THe op to start.
+ * @param attributionSource The identity for which to check the permission.
+ * @param message A message describing the reason the permission was checked
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #finishDataDelivery(Context, String, AttributionSource)
+ */
+ @PermissionResult
+ public static int startOpForDataDelivery(@NonNull Context context,
+ @NonNull String opName, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ final int result = checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
+ message, true /*forDataDelivery*/, true /*startDataDelivery*/);
+ // It is important to finish any started op if some step in the attribution chain failed.
+ if (result != PERMISSION_GRANTED) {
+ finishDataDelivery(context, opName, attributionSource);
+ }
+ return result;
+ }
+
+ /**
+ * Finishes an ongoing op for data access chain described by the given {@link
+ * AttributionSource}.
+ *
+ * @param context Context for accessing resources.
+ * @param op The op to finish.
+ * @param attributionSource The identity for which finish op.
+ *
+ * @see #startOpForDataDelivery(Context, String, AttributionSource, String)
+ * @see #checkPermissionAndStartDataDelivery(Context, String, AttributionSource, String)
+ */
+ public static void finishDataDelivery(@NonNull Context context, @NonNull String op,
+ @NonNull AttributionSource attributionSource) {
+ if (op == null || attributionSource.getPackageName() == null) {
+ return;
+ }
+
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ appOpsManager.finishProxyOp(op, attributionSource);
+
+ if (attributionSource.getNext() != null) {
+ finishDataDelivery(context, op, attributionSource.getNext());
+ }
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link
+ * AttributionSource} has a given app op allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for op checks at the
+ * preflight point where you will not deliver the protected data
+ * to clients but schedule a data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a data listener it should have the op
+ * but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have data
+ * access and this check will not leave a trace that protected data
+ * was delivered. When you are about to deliver the data to a registered
+ * listener you should use {@link #checkOpForDataDelivery(Context, String,
+ * AttributionSource, String)} which will evaluate the op access based
+ * on the current fg/bg state of the app and leave a record that the data was
+ * accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param opName The op to check.
+ * @param attributionSource The identity for which to check the permission.
+ * @param message A message describing the reason the permission was checked
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkOpForDataDelivery(Context, String, AttributionSource, String)
+ */
+ @PermissionResult
+ public static int checkOpForPreflight(@NonNull Context context,
+ @NonNull String opName, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
+ message, false /*forDataDelivery*/, false /*startDataDelivery*/);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has an allowed app op.
+ *
+ * <strong>NOTE:</strong> Use this method only for op checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a data listener it should have the data
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkOpForPreflight(Context, String, AttributionSource, String)}
+ * to determine if the app has or may have data access and this check will not
+ * leave a trace that op protected data was delivered. When you are about to
+ * deliver the data to a registered listener you should use this method which
+ * will evaluate the op access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param opName The op to check.
+ * @param attributionSource The identity for which to check the op.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkOpForPreflight(Context, String, AttributionSource, String)
+ */
+ @PermissionResult
+ public static int checkOpForDataDelivery(@NonNull Context context,
+ @NonNull String opName, @NonNull AttributionSource attributionSource,
+ @Nullable String message) {
+ return checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
+ message, true /*forDataDelivery*/, false /*startDataDelivery*/);
}
/**
@@ -158,8 +504,8 @@
* fg/gb state) and this check will not leave a trace that permission protected data
* was delivered. When you are about to deliver the location data to a registered
* listener you should use {@link #checkPermissionForDataDelivery(Context, String,
- * int, int, String, String)} which will evaluate the permission access based on the current
- * fg/bg state of the app and leave a record that the data was accessed.
+ * int, int, String, String, String)} which will evaluate the permission access based
+ * on the currentfg/bg state of the app and leave a record that the data was accessed.
*
* @param context Context for accessing resources.
* @param permission The permission to check.
@@ -170,13 +516,49 @@
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
*
- * @see #checkPermissionForDataDelivery(Context, String, int, int, String, String)
+ * @see #checkPermissionForDataDelivery(Context, String, int, int, String, String, String)
*/
@PermissionResult
public static int checkPermissionForPreflight(@NonNull Context context,
@NonNull String permission, int pid, int uid, @Nullable String packageName) {
- return checkPermissionCommon(context, permission, pid, uid, packageName,
- null /*attributionTag*/, null /*message*/, false /*forDataDelivery*/);
+ return checkPermissionForPreflight(context, permission, new AttributionSource(
+ uid, packageName, null /*attributionTag*/));
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission and whether the app op that corresponds to this permission
+ * is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a data listener it should have the required
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have the
+ * permission and this check will not leave a trace that permission protected data
+ * was delivered. When you are about to deliver the protected data to a registered
+ * listener you should use {@link #checkPermissionForDataDelivery(Context, String,
+ * int, AttributionSource, String, boolean)} which will evaluate the permission access based
+ * on the current fg/bg state of the app and leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param attributionSource The identity for which to check the permission.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkPermissionForDataDelivery(Context, String, int, AttributionSource,
+ * String, boolean)
+ */
+ @PermissionResult
+ public static int checkPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission, @NonNull AttributionSource attributionSource) {
+ return checkPermissionCommon(context, permission, attributionSource,
+ null /*message*/, false /*forDataDelivery*/, /*startDataDelivery*/ false,
+ /*fromDatasource*/ false);
}
/**
@@ -211,7 +593,8 @@
public static int checkSelfPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, @Nullable String message) {
return checkPermissionForDataDelivery(context, permission, Process.myPid(),
- Process.myUid(), context.getPackageName(), context.getAttributionTag(), message);
+ Process.myUid(), context.getPackageName(), context.getAttributionTag(), message,
+ /*startDataDelivery*/ false);
}
/**
@@ -289,7 +672,8 @@
return PERMISSION_HARD_DENIED;
}
return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
- Binder.getCallingUid(), callingPackageName, callingAttributionTag, message);
+ Binder.getCallingUid(), callingPackageName, callingAttributionTag, message,
+ /*startDataDelivery*/ false);
}
/**
@@ -308,8 +692,8 @@
* fg/gb state) and this check will not leave a trace that permission protected data
* was delivered. When you are about to deliver the location data to a registered
* listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
- * String, String)} which will evaluate the permission access based on the current fg/bg state
- * of the app and leave a record that the data was accessed.
+ * String, String, String, String)} which will evaluate the permission access based on the
+ * current fg/bg stateof the app and leave a record that the data was accessed.
*
* @param context Context for accessing resources.
* @param permission The permission to check.
@@ -318,7 +702,7 @@
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
*
- * @see #checkCallingPermissionForDataDelivery(Context, String, String, String)
+ * @see #checkCallingPermissionForDataDelivery(Context, String, String, String, String)
*/
@PermissionResult
public static int checkCallingPermissionForPreflight(@NonNull Context context,
@@ -370,7 +754,8 @@
callingAttributionTag = (Binder.getCallingPid() == Process.myPid())
? context.getAttributionTag() : callingAttributionTag;
return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
- Binder.getCallingUid(), callingPackageName, callingAttributionTag, message);
+ Binder.getCallingUid(), callingPackageName, callingAttributionTag, message,
+ /*startDataDelivery*/ false);
}
/**
@@ -408,88 +793,325 @@
Binder.getCallingUid(), packageName);
}
- static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
- @Nullable String message, boolean forDataDelivery) {
- final PermissionInfo permissionInfo;
- try {
- // TODO(b/147869157): Cache platform defined app op and runtime permissions to avoid
- // calling into the package manager every time.
- permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0);
- } catch (PackageManager.NameNotFoundException ignored) {
- return PERMISSION_HARD_DENIED;
- }
+ @PermissionResult
+ private static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
+ @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean forDataDelivery, boolean startDataDelivery,
+ boolean fromDatasource) {
+ PermissionInfo permissionInfo = sPlatformPermissions.get(permission);
- if (packageName == null) {
- String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
- if (packageNames != null && packageNames.length > 0) {
- packageName = packageNames[0];
+ if (permissionInfo == null) {
+ try {
+ permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0);
+ if (PLATFORM_PACKAGE_NAME.equals(permissionInfo.packageName)) {
+ // Double addition due to concurrency is fine - the backing store is concurrent.
+ sPlatformPermissions.put(permission, permissionInfo);
+ }
+ } catch (PackageManager.NameNotFoundException ignored) {
+ return PERMISSION_HARD_DENIED;
}
}
if (permissionInfo.isAppOp()) {
- return checkAppOpPermission(context, permission, pid, uid, packageName, attributionTag,
- message, forDataDelivery);
+ return checkAppOpPermission(context, permission, attributionSource, message,
+ forDataDelivery, fromDatasource);
}
if (permissionInfo.isRuntime()) {
- return checkRuntimePermission(context, permission, pid, uid, packageName,
- attributionTag, message, forDataDelivery);
+ return checkRuntimePermission(context, permission, attributionSource, message,
+ forDataDelivery, startDataDelivery, fromDatasource);
}
- return context.checkPermission(permission, pid, uid);
+
+ if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(),
+ attributionSource.getRenouncedPermissions())) {
+ return PERMISSION_HARD_DENIED;
+ }
+
+ if (attributionSource.getNext() != null) {
+ return checkPermissionCommon(context, permission,
+ attributionSource.getNext(), message, forDataDelivery,
+ startDataDelivery, /*fromDatasource*/ false);
+ }
+
+ return PERMISSION_GRANTED;
}
+ @PermissionResult
private static int checkAppOpPermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
- @Nullable String message, boolean forDataDelivery) {
- final String op = AppOpsManager.permissionToOp(permission);
- if (op == null || packageName == null) {
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean fromDatasource) {
+ final int op = AppOpsManager.permissionToOpCode(permission);
+ if (op < 0 || attributionSource.getPackageName() == null) {
return PERMISSION_HARD_DENIED;
}
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- final int opMode = (forDataDelivery)
- ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
- : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
- switch (opMode) {
- case AppOpsManager.MODE_ALLOWED:
- case AppOpsManager.MODE_FOREGROUND: {
- return PERMISSION_GRANTED;
- }
- case AppOpsManager.MODE_DEFAULT: {
- return context.checkPermission(permission, pid, uid)
- == PackageManager.PERMISSION_GRANTED
- ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
- }
- default: {
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (fromDatasource || next != null);
+
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if ((!fromDatasource || current != attributionSource)
+ && next != null && !current.isTrusted(context)) {
return PERMISSION_HARD_DENIED;
}
+
+ int opMode;
+ if (forDataDelivery) {
+ if (next == null) {
+ opMode = appOpsManager.noteOpNoThrow(op, current.getUid(),
+ current.getPackageName(), current.getAttributionTag(), message);
+ } else {
+ opMode = appOpsManager.noteProxyOpNoThrow(op, current, message,
+ skipCurrentChecks);
+ }
+ } else {
+ opMode = appOpsManager.unsafeCheckOpRawNoThrow(op, current.getUid(),
+ current.getPackageName());
+ if (next != null && opMode == AppOpsManager.MODE_ALLOWED) {
+ opMode = appOpsManager.unsafeCheckOpRawNoThrow(op, next.getUid(),
+ next.getPackageName());
+ }
+ }
+
+ switch (opMode) {
+ case AppOpsManager.MODE_IGNORED:
+ case AppOpsManager.MODE_ERRORED: {
+ return PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_DEFAULT: {
+ if (!skipCurrentChecks && !checkPermission(context, permission,
+ attributionSource.getUid(), attributionSource
+ .getRenouncedPermissions())) {
+ return PERMISSION_HARD_DENIED;
+ }
+ if (next != null && !checkPermission(context, permission,
+ next.getUid(), next.getRenouncedPermissions())) {
+ return PERMISSION_HARD_DENIED;
+ }
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
+ return PERMISSION_GRANTED;
+ }
+
+ current = next;
}
}
private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String attributionTag,
- @Nullable String message, boolean forDataDelivery) {
- if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) {
+ // Now let's check the identity chain...
+ final int op = AppOpsManager.permissionToOpCode(permission);
+
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (fromDatasource || next != null);
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if ((!fromDatasource || current != attributionSource)
+ && next != null && !current.isTrusted(context)) {
+ return PERMISSION_HARD_DENIED;
+ }
+
+ // If we already checked the permission for this one, skip the work
+ if (!skipCurrentChecks && !checkPermission(context, permission,
+ current.getUid(), current.getRenouncedPermissions())) {
+ return PERMISSION_HARD_DENIED;
+ }
+
+ if (next != null && !checkPermission(context, permission,
+ next.getUid(), next.getRenouncedPermissions())) {
+ return PERMISSION_HARD_DENIED;
+ }
+
+ if (op < 0) {
+ continue;
+ }
+
+ // The access is for oneself if this is the single receiver of data
+ // after the data source or if this is the single attribution source
+ // in the chain if not from a datasource.
+ final boolean singleReceiverFromDatasource = (fromDatasource
+ && current == attributionSource && next != null && next.getNext() == null);
+ final boolean selfAccess = singleReceiverFromDatasource || next == null;
+
+ final int opMode = performOpTransaction(context, op, current, message,
+ forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
+ singleReceiverFromDatasource);
+
+ switch (opMode) {
+ case AppOpsManager.MODE_ERRORED: {
+ return PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_IGNORED: {
+ return PERMISSION_SOFT_DENIED;
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
+ return PERMISSION_GRANTED;
+ }
+
+ current = next;
+ }
+ }
+
+ private static boolean checkPermission(@NonNull Context context, @NonNull String permission,
+ int uid, @NonNull Set<String> renouncedPermissions) {
+ final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
+ uid) == PackageManager.PERMISSION_GRANTED;
+ if (permissionGranted && renouncedPermissions.contains(permission)) {
+ return false;
+ }
+ return permissionGranted;
+ }
+
+ private static int checkOp(@NonNull Context context, @NonNull int op,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery) {
+ if (op < 0 || attributionSource.getPackageName() == null) {
return PERMISSION_HARD_DENIED;
}
- final String op = AppOpsManager.permissionToOp(permission);
- if (op == null || packageName == null) {
- return PERMISSION_GRANTED;
- }
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
- final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- final int opMode = (forDataDelivery)
- ? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
- : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
+ while (true) {
+ final boolean skipCurrentChecks = (next != null);
+ next = current.getNext();
- switch (opMode) {
- case AppOpsManager.MODE_ALLOWED:
- case AppOpsManager.MODE_FOREGROUND:
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if (next != null && !current.isTrusted(context)) {
+ return PERMISSION_HARD_DENIED;
+ }
+
+ // The access is for oneself if this is the single attribution source in the chain.
+ final boolean selfAccess = (next == null);
+
+ final int opMode = performOpTransaction(context, op, current, message,
+ forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
+ /*fromDatasource*/ false);
+
+ switch (opMode) {
+ case AppOpsManager.MODE_ERRORED: {
+ return PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_IGNORED: {
+ return PERMISSION_SOFT_DENIED;
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
return PERMISSION_GRANTED;
- default:
- return PERMISSION_SOFT_DENIED;
+ }
+
+ current = next;
}
}
+ // If from data source and there is next app after that we need to note SELF of (noteOp) for the app vs proxy
+ private static int performOpTransaction(@NonNull Context context, int op,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation,
+ boolean selfAccess, boolean singleReceiverFromDatasource) {
+ // We cannot perform app ops transactions without a package name. In all relevant
+ // places we pass the package name but just in case there is a bug somewhere we
+ // do a best effort to resolve the package from the UID (pick first without a loss
+ // of generality - they are in the same security sandbox).
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ if (!forDataDelivery) {
+ final String resolvedPackageName = resolvePackageName(context, attributionSource);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ final int opMode = appOpsManager.unsafeCheckOpRawNoThrow(op,
+ attributionSource.getUid(), resolvedPackageName);
+ final AttributionSource previous = attributionSource.getNext();
+ if (opMode == AppOpsManager.MODE_ALLOWED && previous != null) {
+ final String resolvedPreviousPackageName = resolvePackageName(context,
+ previous);
+ if (resolvedPreviousPackageName == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ return appOpsManager.unsafeCheckOpRawNoThrow(op, previous.getUid(),
+ resolvedPreviousPackageName);
+ }
+ return opMode;
+ } else if (startDataDelivery) {
+ final AttributionSource accessorSource = (!singleReceiverFromDatasource)
+ ? attributionSource : attributionSource.getNext();
+ final AttributionSource resolvedAttributionSource = resolveAttributionSource(
+ context, accessorSource);
+ if (resolvedAttributionSource.getPackageName() == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ if (selfAccess) {
+ return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
+ resolvedAttributionSource.getPackageName(),
+ /*startIfModeDefault*/ false,
+ resolvedAttributionSource.getAttributionTag(),
+ message);
+ } else {
+ return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message,
+ skipProxyOperation);
+ }
+ } else {
+ final AttributionSource accessorSource = (!singleReceiverFromDatasource)
+ ? attributionSource : attributionSource.getNext();
+ final AttributionSource resolvedAttributionSource = resolveAttributionSource(
+ context, accessorSource);
+ if (resolvedAttributionSource.getPackageName() == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ if (selfAccess) {
+ return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
+ resolvedAttributionSource.getPackageName(),
+ resolvedAttributionSource.getAttributionTag(),
+ message);
+ } else {
+ return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message,
+ skipProxyOperation);
+ }
+ }
+ }
+
+ private static @Nullable String resolvePackageName(@NonNull Context context,
+ @NonNull AttributionSource attributionSource) {
+ if (attributionSource.getPackageName() != null) {
+ return attributionSource.getPackageName();
+ }
+ final String[] packageNames = context.getPackageManager().getPackagesForUid(
+ attributionSource.getUid());
+ if (packageNames != null) {
+ // This is best effort if the caller doesn't pass a package. The security
+ // sandbox is UID, therefore we pick an arbitrary package.
+ return packageNames[0];
+ }
+ return null;
+ }
+
+ private static @NonNull AttributionSource resolveAttributionSource(
+ @NonNull Context context, @NonNull AttributionSource attributionSource) {
+ if (attributionSource.getPackageName() != null) {
+ return attributionSource;
+ }
+ return new AttributionSource(attributionSource.getUid(),
+ resolvePackageName(context, attributionSource),
+ attributionSource.getAttributionTag(),
+ attributionSource.getToken(),
+ attributionSource.getRenouncedPermissions(),
+ attributionSource.getNext());
+ }
}
diff --git a/core/java/android/content/pm/AppSearchPerson.java b/core/java/android/content/pm/AppSearchPerson.java
index 66295eb..9283e5f 100644
--- a/core/java/android/content/pm/AppSearchPerson.java
+++ b/core/java/android/content/pm/AppSearchPerson.java
@@ -107,7 +107,7 @@
public static class Builder extends GenericDocument.Builder<Builder> {
public Builder(@NonNull final String id) {
- super(id, SCHEMA_TYPE);
+ super(/*namespace=*/ "", id, SCHEMA_TYPE);
}
/** @hide */
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index c04d3be..b2478ca 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -217,9 +217,8 @@
@NonNull
public static AppSearchShortcutInfo instance(@NonNull final ShortcutInfo shortcutInfo) {
Objects.requireNonNull(shortcutInfo);
- return new Builder(shortcutInfo.getId())
+ return new Builder(shortcutInfo.getPackage(), shortcutInfo.getId())
.setActivity(shortcutInfo.getActivity())
- .setNamespace(shortcutInfo.getPackage())
.setShortLabel(shortcutInfo.getShortLabel())
.setShortLabelResId(shortcutInfo.getShortLabelResourceId())
.setShortLabelResName(shortcutInfo.getTitleResName())
@@ -345,8 +344,8 @@
@VisibleForTesting
public static class Builder extends GenericDocument.Builder<Builder> {
- public Builder(String id) {
- super(id, SCHEMA_TYPE);
+ public Builder(String packageName, String id) {
+ super(/*namespace=*/ packageName, id, SCHEMA_TYPE);
}
/**
@@ -574,16 +573,6 @@
/**
* @hide
*/
- public Builder setPackageName(@Nullable final String packageName) {
- if (!TextUtils.isEmpty(packageName)) {
- setNamespace(packageName);
- }
- return this;
- }
-
- /**
- * @hide
- */
public Builder setFlags(@ShortcutInfo.ShortcutFlags final int flags) {
setPropertyLong(KEY_FLAGS, flattenFlags(flags));
return this;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0da453d..5f3ec36 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1363,7 +1363,7 @@
* Indicates if the application has requested GWP-ASan to be enabled, disabled, or left
* unspecified. Processes can override this setting.
*/
- private @GwpAsanMode int gwpAsanMode;
+ private @GwpAsanMode int gwpAsanMode = GWP_ASAN_DEFAULT;
/**
* Default (unspecified) setting of Memtag.
@@ -1402,13 +1402,38 @@
* Indicates if the application has requested Memtag to be enabled, disabled, or left
* unspecified. Processes can override this setting.
*/
- private @MemtagMode int memtagMode;
+ private @MemtagMode int memtagMode = MEMTAG_DEFAULT;
+
+ /**
+ * Default (unspecified) setting of nativeHeapZeroInitialized.
+ */
+ public static final int ZEROINIT_DEFAULT = -1;
+
+ /**
+ * Disable zero-initialization of the native heap in this application or process.
+ */
+ public static final int ZEROINIT_DISABLED = 0;
+
+ /**
+ * Enable zero-initialization of the native heap in this application or process.
+ */
+ public static final int ZEROINIT_ENABLED = 1;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"ZEROINIT_"}, value = {
+ ZEROINIT_DEFAULT,
+ ZEROINIT_DISABLED,
+ ZEROINIT_ENABLED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NativeHeapZeroInitialized {}
/**
* Enable automatic zero-initialization of native heap memory allocations.
*/
- @Nullable
- private Boolean nativeHeapZeroInit;
+ private @NativeHeapZeroInitialized int nativeHeapZeroInitialized = ZEROINIT_DEFAULT;
/**
* If {@code true} this app requests optimized external storage access.
@@ -1570,8 +1595,8 @@
if (memtagMode != MEMTAG_DEFAULT) {
pw.println(prefix + "memtagMode=" + memtagMode);
}
- if (nativeHeapZeroInit != null) {
- pw.println(prefix + "nativeHeapZeroInit=" + nativeHeapZeroInit);
+ if (nativeHeapZeroInitialized != ZEROINIT_DEFAULT) {
+ pw.println(prefix + "nativeHeapZeroInitialized=" + nativeHeapZeroInitialized);
}
if (requestOptimizedExternalStorageAccess != null) {
pw.println(prefix + "requestOptimizedExternalStorageAccess="
@@ -1686,8 +1711,9 @@
if (memtagMode != MEMTAG_DEFAULT) {
proto.write(ApplicationInfoProto.Detail.ENABLE_MEMTAG, memtagMode);
}
- if (nativeHeapZeroInit != null) {
- proto.write(ApplicationInfoProto.Detail.NATIVE_HEAP_ZERO_INIT, nativeHeapZeroInit);
+ if (nativeHeapZeroInitialized != ZEROINIT_DEFAULT) {
+ proto.write(ApplicationInfoProto.Detail.NATIVE_HEAP_ZERO_INIT,
+ nativeHeapZeroInitialized);
}
proto.end(detailToken);
}
@@ -1802,7 +1828,7 @@
zygotePreloadName = orig.zygotePreloadName;
gwpAsanMode = orig.gwpAsanMode;
memtagMode = orig.memtagMode;
- nativeHeapZeroInit = orig.nativeHeapZeroInit;
+ nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
requestOptimizedExternalStorageAccess = orig.requestOptimizedExternalStorageAccess;
}
@@ -1891,7 +1917,7 @@
dest.writeString8(zygotePreloadName);
dest.writeInt(gwpAsanMode);
dest.writeInt(memtagMode);
- sForBoolean.parcel(nativeHeapZeroInit, dest, parcelableFlags);
+ dest.writeInt(nativeHeapZeroInitialized);
sForBoolean.parcel(requestOptimizedExternalStorageAccess, dest, parcelableFlags);
}
@@ -1977,7 +2003,7 @@
zygotePreloadName = source.readString8();
gwpAsanMode = source.readInt();
memtagMode = source.readInt();
- nativeHeapZeroInit = sForBoolean.unparcel(source);
+ nativeHeapZeroInitialized = source.readInt();
requestOptimizedExternalStorageAccess = sForBoolean.unparcel(source);
}
@@ -2382,7 +2408,9 @@
/** {@hide} */ public void setSplitResourcePaths(String[] splitResourcePaths) { splitPublicSourceDirs = splitResourcePaths; }
/** {@hide} */ public void setGwpAsanMode(@GwpAsanMode int value) { gwpAsanMode = value; }
/** {@hide} */ public void setMemtagMode(@MemtagMode int value) { memtagMode = value; }
- /** {@hide} */ public void setNativeHeapZeroInit(@Nullable Boolean value) { nativeHeapZeroInit = value; }
+ /** {@hide} */ public void setNativeHeapZeroInitialized(@NativeHeapZeroInitialized int value) {
+ nativeHeapZeroInitialized = value;
+ }
/** {@hide} */
public void setRequestOptimizedExternalStorageAccess(@Nullable Boolean value) {
requestOptimizedExternalStorageAccess = value;
@@ -2400,8 +2428,22 @@
/** {@hide} */ public String[] getSplitResourcePaths() { return splitPublicSourceDirs; }
@GwpAsanMode
public int getGwpAsanMode() { return gwpAsanMode; }
+
+ /**
+ * Returns whether the application has requested Memtag to be enabled, disabled, or left
+ * unspecified. Processes can override this setting.
+ */
@MemtagMode
- public int getMemtagMode() { return memtagMode; }
- @Nullable
- public Boolean isNativeHeapZeroInit() { return nativeHeapZeroInit; }
+ public int getMemtagMode() {
+ return memtagMode;
+ }
+
+ /**
+ * Returns whether the application has requested automatic zero-initialization of native heap
+ * memory allocations to be enabled or disabled.
+ */
+ @NativeHeapZeroInitialized
+ public int getNativeHeapZeroInitialized() {
+ return nativeHeapZeroInitialized;
+ }
}
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index d688614..37fd3ff 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -90,6 +90,8 @@
String callingPackage, String packageName, in UserHandle user);
IntentSender getShortcutConfigActivityIntent(String callingPackage, in ComponentName component,
in UserHandle user);
+ PendingIntent getShortcutIntent(String callingPackage, String packageName, String shortcutId,
+ in Bundle opts, in UserHandle user);
// Unregister is performed using package installer
void registerPackageInstallerCallback(String callingPackage,
diff --git a/core/java/android/content/pm/InstallationFile.java b/core/java/android/content/pm/InstallationFile.java
index de761ad..e764020 100644
--- a/core/java/android/content/pm/InstallationFile.java
+++ b/core/java/android/content/pm/InstallationFile.java
@@ -18,21 +18,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
/**
* Definition of a file in a streaming installation session.
* You can use this class to retrieve the information of such a file, such as its name, size and
* metadata. These file attributes will be consistent with those used in:
- * {@code PackageInstaller.Session#addFile}, when the file was first added into the session.
- *
- * WARNING: This is a system API to aid internal development.
- * Use at your own risk. It will change or be removed without warning.
+ * {@code android.content.pm.PackageInstaller.Session#addFile}, when the file was first added
+ * into the session.
*
* @see android.content.pm.PackageInstaller.Session#addFile
- * @hide
*/
-@SystemApi
public final class InstallationFile {
private final @NonNull InstallationFileParcel mParcel;
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 80fecc1..8b9b736 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -843,6 +843,30 @@
}
/**
+ * Returns PendingIntent associated with specified shortcut.
+ *
+ * @param packageName The packageName of the shortcut
+ * @param shortcutId The id of the shortcut
+ * @param opts Options to pass to the PendingIntent
+ * @param user The UserHandle of the profile
+ */
+ @Nullable
+ public PendingIntent getShortcutIntent(@NonNull final String packageName,
+ @NonNull final String shortcutId, @Nullable final Bundle opts,
+ @NonNull final UserHandle user) {
+ logErrorForInvalidProfileAccess(user);
+ if (DEBUG) {
+ Log.i(TAG, "GetShortcutIntent " + packageName + "/" + shortcutId + " " + user);
+ }
+ try {
+ return mService.getShortcutIntent(
+ mContext.getPackageName(), packageName, shortcutId, opts, user);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Retrieves a list of config activities for creating {@link ShortcutInfo}.
*
* @param packageName The specific package to query. If null, it checks all installed packages
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 5afec06..0d218c2 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1165,7 +1165,8 @@
}
/**
- * Adds a file to session. On commit this file will be pulled from dataLoader.
+ * Adds a file to session. On commit this file will be pulled from dataLoader {@code
+ * android.service.dataloader.DataLoaderService.DataLoader}.
*
* @param location target location for the file. Possible values:
* {@link #LOCATION_DATA_APP},
@@ -1184,7 +1185,9 @@
* <a href="https://source.android.com/security/apksigning/v4.html">APK Signature Scheme v4</a>
* @throws SecurityException if called after the session has been
* sealed or abandoned
- * @throws IllegalStateException if called for non-callback session
+ * @throws IllegalStateException if called for non-streaming session
+ *
+ * @see android.content.pm.InstallationFile
*/
public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes,
@NonNull byte[] metadata, @Nullable byte[] signature) {
@@ -1205,10 +1208,8 @@
* @param name name of a file, e.g. split.
* @throws SecurityException if called after the session has been
* sealed or abandoned
- * @throws IllegalStateException if called for non-callback session
- * {@hide}
+ * @throws IllegalStateException if called for non-streaming session
*/
- @SystemApi
public void removeFile(@FileLocation int location, @NonNull String name) {
try {
mSession.removeFile(location, name);
@@ -1565,6 +1566,8 @@
public int rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
/** {@hide} */
public boolean forceQueryableOverride;
+ /** {@hide} */
+ public Boolean requireUserAction;
/**
* Construct parameters for a new package install session.
@@ -1607,6 +1610,12 @@
dataLoaderParams = new DataLoaderParams(dataLoaderParamsParcel);
}
rollbackDataPolicy = source.readInt();
+ int requireUserActionInt = source.readInt();
+ requireUserAction = requireUserActionInt == 0
+ ? Boolean.FALSE
+ : requireUserActionInt == 1
+ ? Boolean.TRUE : null;
+
}
/** {@hide} */
@@ -1635,6 +1644,7 @@
ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
ret.dataLoaderParams = dataLoaderParams;
ret.rollbackDataPolicy = rollbackDataPolicy;
+ ret.requireUserAction = requireUserAction;
return ret;
}
@@ -2015,6 +2025,8 @@
* Set the data loader params for the session.
* This also switches installation into data provider mode and disallow direct writes into
* staging folder.
+ *
+ * @see android.service.dataloader.DataLoaderService.DataLoader
*/
public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) {
this.dataLoaderParams = dataLoaderParams;
@@ -2029,6 +2041,41 @@
}
/**
+ * Optionally indicate whether user action should be required when the session is
+ * committed.
+ * <p>
+ * Defaults to {@code true} for installers using the
+ * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES android.permission
+ * #REQUEST_INSTALL_PACKAGES} permission, and {@code false} otherwise. When {@code true},
+ * installers will receive a {@link #STATUS_PENDING_USER_ACTION} callback once the
+ * session is committed, indicating that the user is required for the install to proceed.
+ * <p>
+ * For installers using the {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES
+ * android.permission.REQUEST_INSTALL_PACKAGES} permission, user action will not be
+ * required when the following conditions are met:
+ *
+ * <ul>
+ * <li>{@code requireUserAction} is set to {@code false}.</li>
+ * <li>The being installed targets {@link android.os.Build.VERSION_CODES#Q API 29} or
+ * higher.</li>
+ * <li>The installer is the {@link InstallSourceInfo#getInstallingPackageName()
+ * installer of record} of an existing version of the app (i.e.: this install session
+ * is an app update or the installer is updating itself).</li>
+ * <li>The installer declares the
+ * {@link android.Manifest.permission#UPDATE_PACKAGES_WITHOUT_USER_ACTION android
+ * .permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION} permission.</li>
+ * </ul>
+ * <p>
+ * Note: The target API level requirement will advance in future Android versions.
+ * Session owners should always be prepared to handle {@link #STATUS_PENDING_USER_ACTION}
+ *
+ * @param requireUserAction whether user action should be required.
+ */
+ public void setRequireUserAction(boolean requireUserAction) {
+ this.requireUserAction = requireUserAction;
+ }
+
+ /**
* Sets the install scenario for this session, which describes the expected user journey.
*/
public void setInstallScenario(@InstallScenario int installScenario) {
@@ -2058,6 +2105,7 @@
pw.printPair("isMultiPackage", isMultiPackage);
pw.printPair("isStaged", isStaged);
pw.printPair("forceQueryable", forceQueryableOverride);
+ pw.printPair("requireUserAction", requireUserAction);
pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
pw.printPair("dataLoaderParams", dataLoaderParams);
pw.printPair("rollbackDataPolicy", rollbackDataPolicy);
@@ -2099,6 +2147,10 @@
dest.writeParcelable(null, flags);
}
dest.writeInt(rollbackDataPolicy);
+ dest.writeInt(requireUserAction == Boolean.TRUE
+ ? 1
+ : requireUserAction == Boolean.FALSE
+ ? 0 : 2);
}
public static final Parcelable.Creator<SessionParams>
@@ -2165,6 +2217,31 @@
*/
public static final int STAGED_SESSION_CONFLICT = 4;
+ /** @hide */
+ @IntDef(prefix = {"USER_ACTION"}, value = {
+ USER_ACTION_UNSPECIFIED,
+ USER_ACTION_REQUIRED,
+ USER_ACTION_NOT_REQUIRED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserActionRequirement {}
+
+ /**
+ * The installer did not calling {@link SessionParams#setRequireUserAction(boolean)} to
+ * specify whether user action should be required for the install.
+ */
+ public static final int USER_ACTION_UNSPECIFIED = 0;
+ /**
+ * The installer called {@link SessionParams#setRequireUserAction(boolean)} with
+ * {@code true} to require user action for the install to complete.
+ */
+ public static final int USER_ACTION_REQUIRED = 1;
+ /**
+ * The installer called {@link SessionParams#setRequireUserAction(boolean)} with
+ * {@code false} to request that user action not be required for this install.
+ */
+ public static final int USER_ACTION_NOT_REQUIRED = 2;
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int sessionId;
@@ -2257,6 +2334,9 @@
public int rollbackDataPolicy;
/** {@hide} */
+ public Boolean requireUserAction;
+
+ /** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public SessionInfo() {
}
@@ -2305,6 +2385,11 @@
isCommitted = source.readBoolean();
rollbackDataPolicy = source.readInt();
createdMillis = source.readLong();
+ int requireUserActionInt = source.readInt();
+ requireUserAction = requireUserActionInt == 0
+ ? Boolean.FALSE
+ : requireUserActionInt == 1
+ ? Boolean.TRUE : null;
}
/**
@@ -2804,6 +2889,25 @@
return updatedMillis;
}
+ /**
+ * Whether user action was required by the installer.
+ *
+ * <p>
+ * Note: a return value of {@code USER_ACTION_NOT_REQUIRED} does not guarantee that the
+ * install will not result in user action.
+ *
+ * @return {@link #USER_ACTION_NOT_REQUIRED}, {@link #USER_ACTION_REQUIRED} or
+ * {@link #USER_ACTION_UNSPECIFIED}
+ */
+ @UserActionRequirement
+ public int getRequireUserAction() {
+ return requireUserAction == null
+ ? USER_ACTION_UNSPECIFIED
+ : requireUserAction == Boolean.TRUE
+ ? USER_ACTION_REQUIRED
+ : USER_ACTION_NOT_REQUIRED;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -2849,6 +2953,10 @@
dest.writeBoolean(isCommitted);
dest.writeInt(rollbackDataPolicy);
dest.writeLong(createdMillis);
+ dest.writeInt(requireUserAction == Boolean.TRUE
+ ? 1
+ : requireUserAction == Boolean.FALSE
+ ? 0 : 2);
}
public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d79b66c..21de365 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3511,8 +3511,57 @@
public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
/**
- * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
- * The device has a StrongBox hardware-backed Keystore.
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device implements
+ * the Android Keystore backed by an isolated execution environment. The version indicates
+ * which features are implemented in the isolated execution environment:
+ * <ul>
+ * <li>100: Hardware support for ECDH (see {@link javax.crypto.KeyAgreement}) and support
+ * for app-generated attestation keys (see {@link
+ * android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias(String)}).
+ * <li>41: Hardware enforcement of device-unlocked keys (see {@link
+ * android.security.keystore.KeyGenParameterSpec.Builder#setUnlockedDeviceRequired(boolean)}).
+ * <li>40: Support for wrapped key import (see {@link
+ * android.security.keystore.WrappedKeyEntry}), optional support for ID attestation (see {@link
+ * android.security.keystore.KeyGenParameterSpec.Builder#setDevicePropertiesAttestationIncluded(boolean)}),
+ * attestation (see {@link
+ * android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])}),
+ * AES, HMAC, ECDSA and RSA support where the secret or private key never leaves secure
+ * hardware, and support for requiring user authentication before a key can be used.
+ * </ul>
+ * This feature version is guaranteed to be set for all devices launching with Android 12 and
+ * may be set on devices launching with an earlier version. If the feature version is set, it
+ * will at least have the value 40. If it's not set the device may have a version of
+ * hardware-backed keystore but it may not support all features listed above.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_HARDWARE_KEYSTORE = "android.hardware.hardware_keystore";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures}, {@link #hasSystemFeature(String)}, and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device implements
+ * the Android Keystore backed by a dedicated secure processor referred to as
+ * <a href="https://source.android.com/security/best-practices/hardware#strongbox-keymaster">
+ * StrongBox</a>. If this feature has a version, the version number indicates which features are
+ * implemented in StrongBox:
+ * <ul>
+ * <li>100: Hardware support for ECDH (see {@link javax.crypto.KeyAgreement}) and support
+ * for app-generated attestation keys (see {@link
+ * android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias(String)}).
+ * <li>41: Hardware enforcement of device-unlocked keys (see {@link
+ * android.security.keystore.KeyGenParameterSpec.Builder#setUnlockedDeviceRequired(boolean)}).
+ * <li>40: Support for wrapped key import (see {@link
+ * android.security.keystore.WrappedKeyEntry}), optional support for ID attestation (see {@link
+ * android.security.keystore.KeyGenParameterSpec.Builder#setDevicePropertiesAttestationIncluded(boolean)}),
+ * attestation (see {@link
+ * android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])}),
+ * AES, HMAC, ECDSA and RSA support where the secret or private key never leaves secure
+ * hardware, and support for requiring user authentication before a key can be used.
+ * </ul>
+ * If a device has StrongBox, this feature version number is guaranteed to be set for all
+ * devices launching with Android 12 and may be set on devices launching with an earlier
+ * version. If the feature version is set, it will at least have the value 40. If it's not
+ * set the device may have StrongBox but it may not support all features listed above.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_STRONGBOX_KEYSTORE =
diff --git a/core/java/android/content/pm/ProcessInfo.java b/core/java/android/content/pm/ProcessInfo.java
index 3dd5ee1..632c0f5 100644
--- a/core/java/android/content/pm/ProcessInfo.java
+++ b/core/java/android/content/pm/ProcessInfo.java
@@ -62,8 +62,7 @@
/**
* Enable automatic zero-initialization of native heap memory allocations.
*/
- @Nullable
- public Boolean nativeHeapZeroInit;
+ public @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized;
@Deprecated
public ProcessInfo(@NonNull ProcessInfo orig) {
@@ -71,7 +70,7 @@
this.deniedPermissions = orig.deniedPermissions;
this.gwpAsanMode = orig.gwpAsanMode;
this.memtagMode = orig.memtagMode;
- this.nativeHeapZeroInit = orig.nativeHeapZeroInit;
+ this.nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
}
@@ -101,7 +100,7 @@
* @param memtagMode
* Indicates if the process has requested Memtag to be enabled (in sync or async mode),
* disabled, or left unspecified.
- * @param nativeHeapZeroInit
+ * @param nativeHeapZeroInitialized
* Enable automatic zero-initialization of native heap memory allocations.
*/
@DataClass.Generated.Member
@@ -110,7 +109,7 @@
@Nullable ArraySet<String> deniedPermissions,
@ApplicationInfo.GwpAsanMode int gwpAsanMode,
@ApplicationInfo.MemtagMode int memtagMode,
- @Nullable Boolean nativeHeapZeroInit) {
+ @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized) {
this.name = name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, name);
@@ -121,7 +120,9 @@
this.memtagMode = memtagMode;
com.android.internal.util.AnnotationValidations.validate(
ApplicationInfo.MemtagMode.class, null, memtagMode);
- this.nativeHeapZeroInit = nativeHeapZeroInit;
+ this.nativeHeapZeroInitialized = nativeHeapZeroInitialized;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
// onConstructed(); // You can define this method to get a callback
}
@@ -145,13 +146,12 @@
byte flg = 0;
if (deniedPermissions != null) flg |= 0x2;
- if (nativeHeapZeroInit != null) flg |= 0x10;
dest.writeByte(flg);
dest.writeString(name);
sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
dest.writeInt(gwpAsanMode);
dest.writeInt(memtagMode);
- if (nativeHeapZeroInit != null) dest.writeBoolean(nativeHeapZeroInit);
+ dest.writeInt(nativeHeapZeroInitialized);
}
@Override
@@ -170,7 +170,7 @@
ArraySet<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
int _gwpAsanMode = in.readInt();
int _memtagMode = in.readInt();
- Boolean _nativeHeapZeroInit = (flg & 0x10) == 0 ? null : (Boolean) in.readBoolean();
+ int _nativeHeapZeroInitialized = in.readInt();
this.name = _name;
com.android.internal.util.AnnotationValidations.validate(
@@ -182,7 +182,9 @@
this.memtagMode = _memtagMode;
com.android.internal.util.AnnotationValidations.validate(
ApplicationInfo.MemtagMode.class, null, memtagMode);
- this.nativeHeapZeroInit = _nativeHeapZeroInit;
+ this.nativeHeapZeroInitialized = _nativeHeapZeroInitialized;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
// onConstructed(); // You can define this method to get a callback
}
@@ -202,10 +204,10 @@
};
@DataClass.Generated(
- time = 1611614699049L,
+ time = 1615850184524L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/ProcessInfo.java",
- inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\npublic @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\npublic @android.annotation.Nullable java.lang.Boolean nativeHeapZeroInit\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+ inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\npublic @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\npublic @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java
index 60f3218..23945ee 100644
--- a/core/java/android/content/pm/SuspendDialogInfo.java
+++ b/core/java/android/content/pm/SuspendDialogInfo.java
@@ -65,16 +65,20 @@
private static final String TAG = SuspendDialogInfo.class.getSimpleName();
private static final String XML_ATTR_ICON_RES_ID = "iconResId";
private static final String XML_ATTR_TITLE_RES_ID = "titleResId";
+ private static final String XML_ATTR_TITLE = "title";
private static final String XML_ATTR_DIALOG_MESSAGE_RES_ID = "dialogMessageResId";
private static final String XML_ATTR_DIALOG_MESSAGE = "dialogMessage";
private static final String XML_ATTR_BUTTON_TEXT_RES_ID = "buttonTextResId";
+ private static final String XML_ATTR_BUTTON_TEXT = "buttonText";
private static final String XML_ATTR_BUTTON_ACTION = "buttonAction";
private final int mIconResId;
private final int mTitleResId;
+ private final String mTitle;
private final int mDialogMessageResId;
private final String mDialogMessage;
private final int mNeutralButtonTextResId;
+ private final String mNeutralButtonText;
private final int mNeutralButtonAction;
/**
@@ -129,6 +133,16 @@
}
/**
+ * @return the title to be shown on the dialog. Returns {@code null} if {@link #getTitleResId()}
+ * returns a valid resource id
+ * @hide
+ */
+ @Nullable
+ public String getTitle() {
+ return mTitle;
+ }
+
+ /**
* @return the resource id of the text to be shown in the dialog's body
* @hide
*/
@@ -148,7 +162,7 @@
}
/**
- * @return the text to be shown
+ * @return the text to be shown on the neutral button
* @hide
*/
@StringRes
@@ -157,6 +171,16 @@
}
/**
+ * @return the text to be shown on the neutral button. Returns {@code null} if
+ * {@link #getNeutralButtonTextResId()} returns a valid resource id
+ * @hide
+ */
+ @Nullable
+ public String getNeutralButtonText() {
+ return mNeutralButtonText;
+ }
+
+ /**
* @return The {@link ButtonAction} that happens on tapping this button
* @hide
*/
@@ -174,6 +198,8 @@
}
if (mTitleResId != ID_NULL) {
out.attributeInt(null, XML_ATTR_TITLE_RES_ID, mTitleResId);
+ } else {
+ XmlUtils.writeStringAttribute(out, XML_ATTR_TITLE, mTitle);
}
if (mDialogMessageResId != ID_NULL) {
out.attributeInt(null, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId);
@@ -182,6 +208,8 @@
}
if (mNeutralButtonTextResId != ID_NULL) {
out.attributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId);
+ } else {
+ XmlUtils.writeStringAttribute(out, XML_ATTR_BUTTON_TEXT, mNeutralButtonText);
}
out.attributeInt(null, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction);
}
@@ -194,8 +222,10 @@
try {
final int iconId = in.getAttributeInt(null, XML_ATTR_ICON_RES_ID, ID_NULL);
final int titleId = in.getAttributeInt(null, XML_ATTR_TITLE_RES_ID, ID_NULL);
+ final String title = XmlUtils.readStringAttribute(in, XML_ATTR_TITLE);
final int buttonTextId =
in.getAttributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, ID_NULL);
+ final String buttonText = XmlUtils.readStringAttribute(in, XML_ATTR_BUTTON_TEXT);
final int buttonAction =
in.getAttributeInt(null, XML_ATTR_BUTTON_ACTION, BUTTON_ACTION_MORE_DETAILS);
final int dialogMessageResId =
@@ -207,9 +237,13 @@
}
if (titleId != ID_NULL) {
dialogInfoBuilder.setTitle(titleId);
+ } else if (title != null) {
+ dialogInfoBuilder.setTitle(title);
}
if (buttonTextId != ID_NULL) {
dialogInfoBuilder.setNeutralButtonText(buttonTextId);
+ } else if (buttonText != null) {
+ dialogInfoBuilder.setNeutralButtonText(buttonText);
}
if (dialogMessageResId != ID_NULL) {
dialogInfoBuilder.setMessage(dialogMessageResId);
@@ -227,7 +261,9 @@
public int hashCode() {
int hashCode = mIconResId;
hashCode = 31 * hashCode + mTitleResId;
+ hashCode = 31 * hashCode + Objects.hashCode(mTitle);
hashCode = 31 * hashCode + mNeutralButtonTextResId;
+ hashCode = 31 * hashCode + Objects.hashCode(mNeutralButtonText);
hashCode = 31 * hashCode + mDialogMessageResId;
hashCode = 31 * hashCode + Objects.hashCode(mDialogMessage);
hashCode = 31 * hashCode + mNeutralButtonAction;
@@ -245,10 +281,12 @@
final SuspendDialogInfo otherDialogInfo = (SuspendDialogInfo) obj;
return mIconResId == otherDialogInfo.mIconResId
&& mTitleResId == otherDialogInfo.mTitleResId
+ && Objects.equals(mTitle, otherDialogInfo.mTitle)
&& mDialogMessageResId == otherDialogInfo.mDialogMessageResId
+ && Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage)
&& mNeutralButtonTextResId == otherDialogInfo.mNeutralButtonTextResId
- && mNeutralButtonAction == otherDialogInfo.mNeutralButtonAction
- && Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage);
+ && Objects.equals(mNeutralButtonText, otherDialogInfo.mNeutralButtonText)
+ && mNeutralButtonAction == otherDialogInfo.mNeutralButtonAction;
}
@NonNull
@@ -264,11 +302,19 @@
builder.append("mTitleResId = 0x");
builder.append(Integer.toHexString(mTitleResId));
builder.append(" ");
+ } else if (mTitle != null) {
+ builder.append("mTitle = \"");
+ builder.append(mTitle);
+ builder.append("\"");
}
if (mNeutralButtonTextResId != ID_NULL) {
builder.append("mNeutralButtonTextResId = 0x");
builder.append(Integer.toHexString(mNeutralButtonTextResId));
builder.append(" ");
+ } else if (mNeutralButtonText != null) {
+ builder.append("mNeutralButtonText = \"");
+ builder.append(mNeutralButtonText);
+ builder.append("\"");
}
if (mDialogMessageResId != ID_NULL) {
builder.append("mDialogMessageResId = 0x");
@@ -294,27 +340,33 @@
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeInt(mIconResId);
dest.writeInt(mTitleResId);
+ dest.writeString(mTitle);
dest.writeInt(mDialogMessageResId);
dest.writeString(mDialogMessage);
dest.writeInt(mNeutralButtonTextResId);
+ dest.writeString(mNeutralButtonText);
dest.writeInt(mNeutralButtonAction);
}
private SuspendDialogInfo(Parcel source) {
mIconResId = source.readInt();
mTitleResId = source.readInt();
+ mTitle = source.readString();
mDialogMessageResId = source.readInt();
mDialogMessage = source.readString();
mNeutralButtonTextResId = source.readInt();
+ mNeutralButtonText = source.readString();
mNeutralButtonAction = source.readInt();
}
SuspendDialogInfo(Builder b) {
mIconResId = b.mIconResId;
mTitleResId = b.mTitleResId;
+ mTitle = (mTitleResId == ID_NULL) ? b.mTitle : null;
mDialogMessageResId = b.mDialogMessageResId;
mDialogMessage = (mDialogMessageResId == ID_NULL) ? b.mDialogMessage : null;
mNeutralButtonTextResId = b.mNeutralButtonTextResId;
+ mNeutralButtonText = (mNeutralButtonTextResId == ID_NULL) ? b.mNeutralButtonText : null;
mNeutralButtonAction = b.mNeutralButtonAction;
}
@@ -338,8 +390,10 @@
private int mDialogMessageResId = ID_NULL;
private String mDialogMessage;
private int mTitleResId = ID_NULL;
+ private String mTitle;
private int mIconResId = ID_NULL;
private int mNeutralButtonTextResId = ID_NULL;
+ private String mNeutralButtonText;
private int mNeutralButtonAction = BUTTON_ACTION_MORE_DETAILS;
/**
@@ -370,6 +424,21 @@
}
/**
+ * Set the title text of the dialog. Ignored if a resource id is set via
+ * {@link #setTitle(int)}
+ *
+ * @param title The title of the dialog.
+ * @return this builder object.
+ * @see #setTitle(int)
+ */
+ @NonNull
+ public Builder setTitle(@NonNull String title) {
+ Preconditions.checkStringNotEmpty(title, "Title cannot be null or empty");
+ mTitle = title;
+ return this;
+ }
+
+ /**
* Set the text to show in the body of the dialog. Ignored if a resource id is set via
* {@link #setMessage(int)}.
* <p>
@@ -427,6 +496,22 @@
}
/**
+ * Set the text to be shown on the neutral button. Ignored if a resource id is set via
+ * {@link #setNeutralButtonText(int)}
+ *
+ * @param neutralButtonText The title of the dialog.
+ * @return this builder object.
+ * @see #setNeutralButtonText(int)
+ */
+ @NonNull
+ public Builder setNeutralButtonText(@NonNull String neutralButtonText) {
+ Preconditions.checkStringNotEmpty(neutralButtonText,
+ "Button text cannot be null or empty");
+ mNeutralButtonText = neutralButtonText;
+ return this;
+ }
+
+ /**
* Set the action expected to happen on neutral button tap. Defaults to
* {@link #BUTTON_ACTION_MORE_DETAILS} if this is not provided.
*
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index a3c2cbc..5887047 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -106,7 +106,7 @@
final String packagePath = packageFile.getAbsolutePath();
return input.success(
new PackageLite(packagePath, baseApk.getPath(), baseApk, null,
- null, null, null, null, null));
+ null, null, null, null, null, baseApk.getTargetSdkVersion()));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -242,7 +242,8 @@
splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.getPath();
return input.success(
new PackageLite(codePath, baseCodePath, baseApk, splitNames, isFeatureSplits,
- usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes));
+ usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes,
+ baseApk.getTargetSdkVersion()));
}
/**
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index 803d643..9172555 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -55,6 +55,7 @@
/** Major and minor version number of this package */
private final int mVersionCodeMajor;
private final int mVersionCode;
+ private final int mTargetSdk;
/** Revision code of base APK */
private final int mBaseRevisionCode;
/** Revision codes of any split APKs, ordered by parsed splitName */
@@ -99,7 +100,8 @@
public PackageLite(String path, String baseApkPath, ApkLite baseApk,
String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames,
- String[] configForSplit, String[] splitApkPaths, int[] splitRevisionCodes) {
+ String[] configForSplit, String[] splitApkPaths, int[] splitRevisionCodes,
+ int targetSdk) {
// The following paths may be different from the path in ApkLite because we
// move or rename the APK files. Use parameters to indicate the correct paths.
mPath = path;
@@ -125,6 +127,7 @@
mConfigForSplit = configForSplit;
mSplitApkPaths = splitApkPaths;
mSplitRevisionCodes = splitRevisionCodes;
+ mTargetSdk = targetSdk;
}
/**
@@ -230,6 +233,11 @@
return mVersionCode;
}
+ @DataClass.Generated.Member
+ public int getTargetSdk() {
+ return mTargetSdk;
+ }
+
/**
* Revision code of base APK
*/
@@ -349,10 +357,10 @@
}
@DataClass.Generated(
- time = 1610596639255L,
+ time = 1615914120261L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 4dc9ce8..1c65e00 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
@@ -251,11 +252,12 @@
ParsingPackage setEnabled(boolean enabled);
- ParsingPackage setGwpAsanMode(int gwpAsanMode);
+ ParsingPackage setGwpAsanMode(@ApplicationInfo.GwpAsanMode int gwpAsanMode);
- ParsingPackage setMemtagMode(int memtagMode);
+ ParsingPackage setMemtagMode(@ApplicationInfo.MemtagMode int memtagMode);
- ParsingPackage setNativeHeapZeroInit(@Nullable Boolean nativeHeapZeroInit);
+ ParsingPackage setNativeHeapZeroInitialized(
+ @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized);
ParsingPackage setRequestOptimizedExternalStorageAccess(
@Nullable Boolean requestOptimizedExternalStorageAccess);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 065ed2e..60aac76 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -382,12 +382,14 @@
private int autoRevokePermissions;
- protected int gwpAsanMode;
- protected int memtagMode;
+ @ApplicationInfo.GwpAsanMode
+ private int gwpAsanMode;
- @Nullable
- @DataClass.ParcelWith(ForBoolean.class)
- private Boolean nativeHeapZeroInit;
+ @ApplicationInfo.MemtagMode
+ private int memtagMode;
+
+ @ApplicationInfo.NativeHeapZeroInitialized
+ private int nativeHeapZeroInitialized;
@Nullable
@DataClass.ParcelWith(ForBoolean.class)
@@ -1071,7 +1073,7 @@
appInfo.zygotePreloadName = zygotePreloadName;
appInfo.setGwpAsanMode(gwpAsanMode);
appInfo.setMemtagMode(memtagMode);
- appInfo.setNativeHeapZeroInit(nativeHeapZeroInit);
+ appInfo.setNativeHeapZeroInitialized(nativeHeapZeroInitialized);
appInfo.setRequestOptimizedExternalStorageAccess(requestOptimizedExternalStorageAccess);
appInfo.setBaseCodePath(mBaseApkPath);
appInfo.setBaseResourcePath(mBaseApkPath);
@@ -1207,7 +1209,7 @@
dest.writeLong(this.mBooleans);
dest.writeMap(this.mProperties);
dest.writeInt(this.memtagMode);
- sForBoolean.parcel(this.nativeHeapZeroInit, dest, flags);
+ dest.writeInt(this.nativeHeapZeroInitialized);
sForBoolean.parcel(this.requestOptimizedExternalStorageAccess, dest, flags);
}
@@ -1331,7 +1333,7 @@
this.mBooleans = in.readLong();
this.mProperties = in.createTypedArrayMap(Property.CREATOR);
this.memtagMode = in.readInt();
- this.nativeHeapZeroInit = sForBoolean.unparcel(in);
+ this.nativeHeapZeroInitialized = in.readInt();
this.requestOptimizedExternalStorageAccess = sForBoolean.unparcel(in);
assignDerivedFields();
}
@@ -2096,20 +2098,22 @@
return getBoolean(Booleans.DIRECT_BOOT_AWARE);
}
+ @ApplicationInfo.GwpAsanMode
@Override
public int getGwpAsanMode() {
return gwpAsanMode;
}
+ @ApplicationInfo.MemtagMode
@Override
public int getMemtagMode() {
return memtagMode;
}
- @Nullable
+ @ApplicationInfo.NativeHeapZeroInitialized
@Override
- public Boolean isNativeHeapZeroInit() {
- return nativeHeapZeroInit;
+ public int getNativeHeapZeroInitialized() {
+ return nativeHeapZeroInitialized;
}
@Nullable
@@ -2550,20 +2554,21 @@
}
@Override
- public ParsingPackageImpl setGwpAsanMode(int value) {
+ public ParsingPackageImpl setGwpAsanMode(@ApplicationInfo.GwpAsanMode int value) {
gwpAsanMode = value;
return this;
}
@Override
- public ParsingPackageImpl setMemtagMode(int value) {
+ public ParsingPackageImpl setMemtagMode(@ApplicationInfo.MemtagMode int value) {
memtagMode = value;
return this;
}
@Override
- public ParsingPackageImpl setNativeHeapZeroInit(@Nullable Boolean value) {
- nativeHeapZeroInit = value;
+ public ParsingPackageImpl setNativeHeapZeroInitialized(
+ @ApplicationInfo.NativeHeapZeroInitialized int value) {
+ nativeHeapZeroInitialized = value;
return this;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 47dfa9d..cfd828e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -887,20 +887,22 @@
* @see ApplicationInfo#gwpAsanMode
* @see R.styleable#AndroidManifest_gwpAsanMode
*/
+ @ApplicationInfo.GwpAsanMode
int getGwpAsanMode();
/**
* @see ApplicationInfo#memtagMode
* @see R.styleable#AndroidManifest_memtagMode
*/
+ @ApplicationInfo.MemtagMode
int getMemtagMode();
- /**
- * @see ApplicationInfo#nativeHeapZeroInit
- * @see R.styleable#AndroidManifest_nativeHeapZeroInit
+ /**
+ * @see ApplicationInfo#nativeHeapZeroInitialized
+ * @see R.styleable#AndroidManifest_nativeHeapZeroInitialized
*/
- @Nullable
- Boolean isNativeHeapZeroInit();
+ @ApplicationInfo.NativeHeapZeroInitialized
+ int getNativeHeapZeroInitialized();
@Nullable
Boolean hasRequestOptimizedExternalStorageAccess();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 0e1574c..4e7bd70 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -34,6 +34,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleableRes;
+import android.app.ActivityThread;
+import android.app.ResourcesManager;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
@@ -85,7 +87,9 @@
import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
+import android.os.RemoteException;
import android.os.Trace;
+import android.os.UserHandle;
import android.os.ext.SdkExtensions;
import android.permission.PermissionManager;
import android.text.TextUtils;
@@ -2008,9 +2012,11 @@
pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
- if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInit)) {
- pkg.setNativeHeapZeroInit(sa.getBoolean(
- R.styleable.AndroidManifestApplication_nativeHeapZeroInit, false));
+ if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) {
+ Boolean v = sa.getBoolean(
+ R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false);
+ pkg.setNativeHeapZeroInitialized(
+ v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
}
if (sa.hasValue(
R.styleable.AndroidManifestApplication_requestOptimizedExternalStorageAccess)) {
@@ -3041,6 +3047,42 @@
}
}
+ /**
+ * @hide
+ */
+ public static void readConfigUseRoundIcon(Resources r) {
+ if (r != null) {
+ sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
+ return;
+ }
+
+ final ApplicationInfo androidAppInfo;
+ try {
+ androidAppInfo = ActivityThread.getPackageManager().getApplicationInfo(
+ "android", 0 /* flags */,
+ UserHandle.myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ final Resources systemResources = Resources.getSystem();
+
+ // Create in-flight as this overlayable resource is only used when config changes
+ final Resources overlayableRes = ResourcesManager.getInstance().getResources(
+ null /* activityToken */,
+ null /* resDir */,
+ null /* splitResDirs */,
+ androidAppInfo.resourceDirs,
+ androidAppInfo.overlayPaths,
+ androidAppInfo.sharedLibraryFiles,
+ null /* overrideDisplayId */,
+ null /* overrideConfig */,
+ systemResources.getCompatibilityInfo(),
+ systemResources.getClassLoader(),
+ null /* loaders */);
+
+ sUseRoundIcon = overlayableRes.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
+ }
+
/*
The following set of methods makes code easier to read by re-ordering the TypedArray methods.
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index 89fef9d..54a60d3 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -42,10 +42,12 @@
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
protected Set<String> deniedPermissions = emptySet();
+ @ApplicationInfo.GwpAsanMode
protected int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
+ @ApplicationInfo.MemtagMode
protected int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
- @Nullable
- protected Boolean nativeHeapZeroInit = null;
+ @ApplicationInfo.NativeHeapZeroInitialized
+ protected int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
public ParsedProcess() {
}
@@ -78,9 +80,9 @@
public ParsedProcess(
@NonNull String name,
@NonNull Set<String> deniedPermissions,
- int gwpAsanMode,
- int memtagMode,
- @Nullable Boolean nativeHeapZeroInit) {
+ @ApplicationInfo.GwpAsanMode int gwpAsanMode,
+ @ApplicationInfo.MemtagMode int memtagMode,
+ @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized) {
this.name = name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, name);
@@ -88,8 +90,14 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, deniedPermissions);
this.gwpAsanMode = gwpAsanMode;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
this.memtagMode = memtagMode;
- this.nativeHeapZeroInit = nativeHeapZeroInit;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.MemtagMode.class, null, memtagMode);
+ this.nativeHeapZeroInitialized = nativeHeapZeroInitialized;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
// onConstructed(); // You can define this method to get a callback
}
@@ -105,18 +113,18 @@
}
@DataClass.Generated.Member
- public int getGwpAsanMode() {
+ public @ApplicationInfo.GwpAsanMode int getGwpAsanMode() {
return gwpAsanMode;
}
@DataClass.Generated.Member
- public int getMemtagMode() {
+ public @ApplicationInfo.MemtagMode int getMemtagMode() {
return memtagMode;
}
@DataClass.Generated.Member
- public @Nullable Boolean getNativeHeapZeroInit() {
- return nativeHeapZeroInit;
+ public @ApplicationInfo.NativeHeapZeroInitialized int getNativeHeapZeroInitialized() {
+ return nativeHeapZeroInitialized;
}
@DataClass.Generated.Member
@@ -136,14 +144,11 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
- byte flg = 0;
- if (nativeHeapZeroInit != null) flg |= 0x10;
- dest.writeByte(flg);
dest.writeString(name);
sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
dest.writeInt(gwpAsanMode);
dest.writeInt(memtagMode);
- if (nativeHeapZeroInit != null) dest.writeBoolean(nativeHeapZeroInit);
+ dest.writeInt(nativeHeapZeroInitialized);
}
@Override
@@ -157,12 +162,11 @@
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
- byte flg = in.readByte();
String _name = in.readString();
Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
int _gwpAsanMode = in.readInt();
int _memtagMode = in.readInt();
- Boolean _nativeHeapZeroInit = (flg & 0x10) == 0 ? null : (Boolean) in.readBoolean();
+ int _nativeHeapZeroInitialized = in.readInt();
this.name = _name;
com.android.internal.util.AnnotationValidations.validate(
@@ -171,8 +175,14 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, deniedPermissions);
this.gwpAsanMode = _gwpAsanMode;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
this.memtagMode = _memtagMode;
- this.nativeHeapZeroInit = _nativeHeapZeroInit;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.MemtagMode.class, null, memtagMode);
+ this.nativeHeapZeroInitialized = _nativeHeapZeroInitialized;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
// onConstructed(); // You can define this method to get a callback
}
@@ -192,10 +202,10 @@
};
@DataClass.Generated(
- time = 1611615591258L,
+ time = 1615850515058L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
- inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected int gwpAsanMode\nprotected int memtagMode\nprotected @android.annotation.Nullable java.lang.Boolean nativeHeapZeroInit\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+ inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprotected @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprotected @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index 2579774..e417e74 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -17,6 +17,7 @@
package android.content.pm.parsing.component;
import android.annotation.NonNull;
+import android.content.pm.ApplicationInfo;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingUtils;
import android.content.pm.parsing.result.ParseInput;
@@ -104,9 +105,11 @@
proc.gwpAsanMode = sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1);
proc.memtagMode = sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1);
- if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInit)) {
- proc.nativeHeapZeroInit =
- sa.getBoolean(R.styleable.AndroidManifestProcess_nativeHeapZeroInit, false);
+ if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
+ Boolean v = sa.getBoolean(
+ R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized, false);
+ proc.nativeHeapZeroInitialized =
+ v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED;
}
} finally {
sa.recycle();
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index d2d1441..33920c6 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -179,10 +179,7 @@
*/
@SystemApi
@Nullable
- @RequiresPermission(anyOf = {
- android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
- android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
- })
+ @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
throws NameNotFoundException {
try {
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 921c630..cdd8265 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -34,6 +34,7 @@
import android.util.Xml;
import com.android.internal.R;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
@@ -112,6 +113,18 @@
* android:color="?android:attr/colorAccent"
* android:alpha="0.5" />
* </pre>
+ * <p>
+ * Starting with API 31, items may optionally define an {@link android.R.attr#lStar android:lStar}
+ * attribute to modify the base color's perceptual luminance. This attribute takes a either
+ * floating-point value between 0 and 100 or a theme attribute that resolves as such. The item's
+ * overall color is calculated by converting the base color to an accessibility friendly color space
+ * and setting its L* to the value specified on the {@code lStar} attribute. For
+ * example, the following item represents the theme's accent color at 50% perpectual luminosity:
+ * <pre>
+ * <item android:state_enabled="false"
+ * android:color="?android:attr/colorAccent"
+ * android:lStar="50" />
+ * </pre>
*
* <a name="DeveloperGuide"></a>
* <h3>Developer guide</h3>
@@ -122,6 +135,7 @@
*
* @attr ref android.R.styleable#ColorStateListItem_alpha
* @attr ref android.R.styleable#ColorStateListItem_color
+ * @attr ref android.R.styleable#ColorStateListItem_lStar
*/
public class ColorStateList extends ComplexColor implements Parcelable {
private static final String TAG = "ColorStateList";
@@ -301,6 +315,24 @@
}
/**
+ * Creates a new ColorStateList that has the same states and colors as this
+ * one but where each color has the specified perceived luminosity value (0-100).
+ *
+ * @param lStar Target perceptual luminance (0-100).
+ * @return A new color state list.
+ */
+ @NonNull
+ public ColorStateList withLStar(float lStar) {
+ final int[] colors = new int[mColors.length];
+ final int len = colors.length;
+ for (int i = 0; i < len; i++) {
+ colors[i] = modulateColor(mColors[i], 1.0f /* alphaMod */, lStar);
+ }
+
+ return new ColorStateList(mStateSpecs, colors);
+ }
+
+ /**
* Fill in this object based on the contents of an XML "selector" element.
*/
private void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
@@ -332,6 +364,7 @@
final int[] themeAttrs = a.extractThemeAttrs();
final int baseColor = a.getColor(R.styleable.ColorStateListItem_color, Color.MAGENTA);
final float alphaMod = a.getFloat(R.styleable.ColorStateListItem_alpha, 1.0f);
+ final float lStar = a.getFloat(R.styleable.ColorStateListItem_lStar, -1.0f);
changingConfigurations |= a.getChangingConfigurations();
@@ -346,6 +379,7 @@
switch (stateResId) {
case R.attr.color:
case R.attr.alpha:
+ case R.attr.lStar:
// Recognized attribute, ignore.
break;
default:
@@ -355,10 +389,11 @@
}
stateSpec = StateSet.trimStateSet(stateSpec, j);
- // Apply alpha modulation. If we couldn't resolve the color or
+ // Apply alpha and luma modulation. If we couldn't resolve the color or
// alpha yet, the default values leave us enough information to
// modulate again during applyTheme().
- final int color = modulateColorAlpha(baseColor, alphaMod);
+ final int color = modulateColor(baseColor, alphaMod, lStar);
+
if (listSize == 0 || stateSpec.length == 0) {
defaultColor = color;
}
@@ -455,7 +490,9 @@
R.styleable.ColorStateListItem_color, mColors[i]);
final float alphaMod = a.getFloat(
R.styleable.ColorStateListItem_alpha, defaultAlphaMod);
- mColors[i] = modulateColorAlpha(baseColor, alphaMod);
+ final float lStar = a.getFloat(
+ R.styleable.ColorStateListItem_lStar, -1.0f);
+ mColors[i] = modulateColor(baseColor, alphaMod, lStar);
// Account for any configuration changes.
mChangingConfigurations |= a.getChangingConfigurations();
@@ -505,13 +542,21 @@
return super.getChangingConfigurations() | mChangingConfigurations;
}
- private int modulateColorAlpha(int baseColor, float alphaMod) {
- if (alphaMod == 1.0f) {
+ private int modulateColor(int baseColor, float alphaMod, float lStar) {
+ final boolean validLStar = lStar >= 0.0f && lStar <= 100.0f;
+ if (alphaMod == 1.0f && !validLStar) {
return baseColor;
}
final int baseAlpha = Color.alpha(baseColor);
final int alpha = MathUtils.constrain((int) (baseAlpha * alphaMod + 0.5f), 0, 255);
+
+ if (validLStar) {
+ final double[] labColor = new double[3];
+ ColorUtils.colorToLAB(baseColor, labColor);
+ baseColor = ColorUtils.LABToColor(lStar, labColor[1], labColor[2]);
+ }
+
return (baseColor & 0xFFFFFF) | (alpha << 24);
}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 5eaa766..2a349e9 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -46,7 +46,7 @@
* The indices used to retrieve values from this structure correspond to
* the positions of the attributes given to obtainStyledAttributes.
*/
-public class TypedArray {
+public class TypedArray implements AutoCloseable {
static TypedArray obtain(Resources res, int len) {
TypedArray attrs = res.mTypedArrayPool.acquire();
@@ -1253,6 +1253,17 @@
}
/**
+ * Recycles the TypedArray, to be re-used by a later caller. After calling
+ * this function you must not ever touch the typed array again.
+ *
+ * @see #recycle()
+ * @throws RuntimeException if the TypedArray has already been recycled.
+ */
+ public void close() {
+ recycle();
+ }
+
+ /**
* Extracts theme attributes from a typed array for later resolution using
* {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}.
* Removes the entries from the typed array so that subsequent calls to typed
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index e03c1f4..d30848f 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -22,6 +22,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -229,7 +230,25 @@
@RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor,
@NonNull OnSensorPrivacyChangedListener listener) {
- addSensorPrivacyListener(sensor, mContext.getMainExecutor(), listener);
+ addSensorPrivacyListener(sensor, mContext.getUserId(), mContext.getMainExecutor(),
+ listener);
+ }
+
+ /**
+ * Registers a new listener to receive notification when the state of sensor privacy
+ * changes.
+ *
+ * @param sensor the sensor to listen to changes to
+ * @param userId the user's id
+ * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
+ * privacy changes.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId,
+ @NonNull OnSensorPrivacyChangedListener listener) {
+ addSensorPrivacyListener(sensor, userId, mContext.getMainExecutor(), listener);
}
/**
@@ -248,6 +267,24 @@
@RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
@NonNull OnSensorPrivacyChangedListener listener) {
+ addSensorPrivacyListener(sensor, mContext.getUserId(), executor, listener);
+ }
+
+ /**
+ * Registers a new listener to receive notification when the state of sensor privacy
+ * changes.
+ *
+ * @param sensor the sensor to listen to changes to
+ * @param executor the executor to dispatch the callback on
+ * @param userId the user's id
+ * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
+ * privacy changes.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId,
+ @NonNull Executor executor, @NonNull OnSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
ISensorPrivacyListener iListener = mListeners.get(listener);
if (iListener == null) {
@@ -261,7 +298,7 @@
}
try {
- mService.addIndividualSensorPrivacyListener(mContext.getUserId(), sensor,
+ mService.addIndividualSensorPrivacyListener(userId, sensor,
iListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -322,8 +359,20 @@
@TestApi
@RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
+ return isSensorPrivacyEnabled(sensor, mContext.getUserId());
+ }
+
+ /**
+ * Returns whether sensor privacy is currently enabled for a specific sensor.
+ *
+ * @return true if sensor privacy is currently enabled, false otherwise.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor, @UserIdInt int userId) {
try {
- return mService.isIndividualSensorPrivacyEnabled(mContext.getUserId(), sensor);
+ return mService.isIndividualSensorPrivacyEnabled(userId, sensor);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -340,8 +389,23 @@
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable) {
+ setSensorPrivacy(sensor, enable, mContext.getUserId());
+ }
+
+ /**
+ * Sets sensor privacy to the specified state for an individual sensor.
+ *
+ * @param sensor the sensor which to change the state for
+ * @param enable the state to which sensor privacy should be set.
+ * @param userId the user's id
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable,
+ @UserIdInt int userId) {
try {
- mService.setIndividualSensorPrivacy(mContext.getUserId(), sensor, enable);
+ mService.setIndividualSensorPrivacy(userId, sensor, enable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -360,8 +424,24 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor,
boolean enable) {
+ setSensorPrivacyForProfileGroup(sensor, enable, mContext.getUserId());
+ }
+
+ /**
+ * Sets sensor privacy to the specified state for an individual sensor for the profile group of
+ * context's user.
+ *
+ * @param sensor the sensor which to change the state for
+ * @param enable the state to which sensor privacy should be set.
+ * @param userId the user's id
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor,
+ boolean enable, @UserIdInt int userId) {
try {
- mService.setIndividualSensorPrivacyForProfileGroup(mContext.getUserId(), sensor,
+ mService.setIndividualSensorPrivacyForProfileGroup(userId, sensor,
enable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -379,8 +459,23 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void suppressSensorPrivacyReminders(@NonNull String packageName,
boolean suppress) {
+ suppressSensorPrivacyReminders(packageName, suppress, mContext.getUserId());
+ }
+
+ /**
+ * Don't show dialogs to turn off sensor privacy for this package.
+ *
+ * @param packageName Package name not to show dialogs for
+ * @param suppress Whether to suppress or re-enable.
+ * @param userId the user's id
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ public void suppressSensorPrivacyReminders(@NonNull String packageName,
+ boolean suppress, @UserIdInt int userId) {
try {
- mService.suppressIndividualSensorPrivacyReminders(mContext.getUserId(), packageName,
+ mService.suppressIndividualSensorPrivacyReminders(userId, packageName,
token, suppress);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 07ebbaf..6654c2c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1210,6 +1210,25 @@
new Key<android.util.Range<Float>>("android.control.zoomRatioRange", new TypeReference<android.util.Range<Float>>() {{ }});
/**
+ * <p>List of available high speed video size, fps range and max batch size configurations
+ * supported by the camera device, in the format of
+ * (width, height, fps_min, fps_max, batch_size_max),
+ * when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to android.control.availableHighSpeedVideoConfigurations, for configurations
+ * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Range of valid values:</b><br></p>
+ * <p>For each configuration, the fps_max >= 120fps.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.HighSpeedVideoConfiguration[]> CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.HighSpeedVideoConfiguration[]>("android.control.availableHighSpeedVideoConfigurationsMaximumResolution", android.hardware.camera2.params.HighSpeedVideoConfiguration[].class);
+
+ /**
* <p>List of edge enhancement modes for {@link CaptureRequest#EDGE_MODE android.edge.mode} that are supported by this camera
* device.</p>
* <p>Full-capability camera devices must always support OFF; camera devices that support
@@ -1770,6 +1789,48 @@
new Key<float[]>("android.lens.distortion", float[].class);
/**
+ * <p>The correction coefficients to correct for this camera device's
+ * radial and tangential lens distortion for a
+ * CaptureRequest with {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Units</b>:
+ * Unitless coefficients.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
+ *
+ * @see CameraCharacteristics#LENS_DISTORTION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<float[]> LENS_DISTORTION_MAXIMUM_RESOLUTION =
+ new Key<float[]>("android.lens.distortionMaximumResolution", float[].class);
+
+ /**
+ * <p>The parameters for this camera device's intrinsic
+ * calibration when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Units</b>:
+ * Pixels in the
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution}
+ * coordinate system.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
+ *
+ * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<float[]> LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION =
+ new Key<float[]>("android.lens.intrinsicCalibrationMaximumResolution", float[].class);
+
+ /**
* <p>List of noise reduction modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} that are supported
* by this camera device.</p>
* <p>Full-capability camera devices will always support OFF and FAST.</p>
@@ -2056,6 +2117,8 @@
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA SECURE_IMAGE_DATA}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA SYSTEM_CAMERA}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING OFFLINE_PROCESSING}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR ULTRA_HIGH_RESOLUTION_SENSOR}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING REMOSAIC_REPROCESSING}</li>
* </ul>
*
* <p>This key is available on all devices.</p>
@@ -2077,6 +2140,8 @@
* @see #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA
* @see #REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA
* @see #REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING
*/
@PublicKey
@NonNull
@@ -2535,8 +2600,6 @@
* set to either OFF or FAST.</p>
* <p>When multiple streams are used in a request, the minimum frame
* duration will be max(individual stream min durations).</p>
- * <p>The minimum frame duration of a stream (of a particular format, size)
- * is the same regardless of whether the stream is input or output.</p>
* <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
* android.scaler.availableStallDurations for more details about
* calculating the max frame rate.</p>
@@ -2916,10 +2979,10 @@
* configurations which belong to this physical camera, and it will advertise and will only
* advertise the maximum supported resolutions for a particular format.</p>
* <p>If this camera device isn't a physical camera device constituting a logical camera,
- * but a standalone ULTRA_HIGH_RESOLUTION_SENSOR camera, this field represents the
- * multi-resolution input/output stream configurations of default mode and max resolution
- * modes. The sizes will be the maximum resolution of a particular format for default mode
- * and max resolution mode.</p>
+ * but a standalone {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * camera, this field represents the multi-resolution input/output stream configurations of
+ * default mode and max resolution modes. The sizes will be the maximum resolution of a
+ * particular format for default mode and max resolution mode.</p>
* <p>This field will only be advertised if the device is a physical camera of a
* logical multi-camera device or an ultra high resolution sensor camera. For a logical
* multi-camera, the camera API will derive the logical camera’s multi-resolution stream
@@ -2977,6 +3040,132 @@
new Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap>("android.scaler.multiResolutionStreamConfigurationMap", android.hardware.camera2.params.MultiResolutionStreamConfigurationMap.class);
/**
+ * <p>The available stream configurations that this
+ * camera device supports (i.e. format, width, height, output/input stream) for a
+ * CaptureRequest with {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to android.scaler.availableStreamConfigurations, for configurations
+ * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Not all output formats may be supported in a configuration with
+ * an input stream of a particular format. For more details, see
+ * android.scaler.availableInputOutputFormatsMapMaximumResolution.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.scaler.availableStreamConfigurationsMaximumResolution", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination when the camera device is sent a CaptureRequest with
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to android.scaler.availableMinFrameDurations, for configurations
+ * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>When multiple streams are used in a request (if supported, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}
+ * is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }), the
+ * minimum frame duration will be max(individual stream min durations).</p>
+ * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
+ * android.scaler.availableStallDurationsMaximumResolution for more details about
+ * calculating the max frame rate.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.scaler.availableMinFrameDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination when CaptureRequests are submitted with
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }</p>
+ * <p>Analogous to android.scaler.availableMinFrameDurations, for configurations
+ * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.scaler.availableStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>The available stream configurations that this
+ * camera device supports when given a CaptureRequest with {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}
+ * set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION };
+ * also includes the minimum frame durations
+ * and the stall durations for each format/size combination.</p>
+ * <p>Analogous to {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationMap>("android.scaler.streamConfigurationMapMaximumResolution", android.hardware.camera2.params.StreamConfigurationMap.class);
+
+ /**
+ * <p>The mapping of image formats that are supported by this
+ * camera device for input streams, to their corresponding output formats, when
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to android.scaler.availableInputOutputFormatsMap for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.ReprocessFormatsMap> SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.ReprocessFormatsMap>("android.scaler.availableInputOutputFormatsMapMaximumResolution", android.hardware.camera2.params.ReprocessFormatsMap.class);
+
+ /**
+ * <p>An array of mandatory stream combinations which are applicable when
+ * {@link android.hardware.camera2.CaptureRequest } has {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set
+ * to {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.
+ * This is an app-readable conversion of the maximum resolution mandatory stream combination
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
+ * <p>The array of
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+ * generated according to the documented
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each
+ * device which has the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability.
+ * Clients can use the array as a quick reference to find an appropriate camera stream
+ * combination.
+ * The mandatory stream combination array will be {@code null} in case the device is not an
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * device.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS =
+ new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryMaximumResolutionStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
+
+ /**
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
* <p>This is the rectangle representing the size of the active region of the sensor (i.e.
@@ -3292,6 +3481,101 @@
new Key<android.graphics.Rect>("android.sensor.info.preCorrectionActiveArraySize", android.graphics.Rect.class);
/**
+ * <p>The area of the image sensor which corresponds to active pixels after any geometric
+ * distortion correction has been applied, when the sensor runs in maximum resolution mode.</p>
+ * <p>Analogous to {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}
+ * is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.
+ * Refer to {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} for details, with sensor array related keys
+ * replaced with their
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+ * counterparts.
+ * This key will only be present for devices which advertise the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability.</p>
+ * <p><b>Units</b>: Pixel coordinates on the image sensor</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION =
+ new Key<android.graphics.Rect>("android.sensor.info.activeArraySizeMaximumResolution", android.graphics.Rect.class);
+
+ /**
+ * <p>Dimensions of the full pixel array, possibly
+ * including black calibration pixels, when the sensor runs in maximum resolution mode.
+ * Analogous to {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+ * set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>The pixel count of the full pixel array of the image sensor, which covers
+ * {@link CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE android.sensor.info.physicalSize} area. This represents the full pixel dimensions of
+ * the raw buffers produced by this sensor, when it runs in maximum resolution mode. That
+ * is, when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.
+ * This key will only be present for devices which advertise the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability.</p>
+ * <p><b>Units</b>: Pixels</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION =
+ new Key<android.util.Size>("android.sensor.info.pixelArraySizeMaximumResolution", android.util.Size.class);
+
+ /**
+ * <p>The area of the image sensor which corresponds to active pixels prior to the
+ * application of any geometric distortion correction, when the sensor runs in maximum
+ * resolution mode. This key must be used for crop / metering regions, only when
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize},
+ * when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.
+ * This key will only be present for devices which advertise the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability.</p>
+ * <p><b>Units</b>: Pixel coordinates on the image sensor</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<android.graphics.Rect> SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION =
+ new Key<android.graphics.Rect>("android.sensor.info.preCorrectionActiveArraySizeMaximumResolution", android.graphics.Rect.class);
+
+ /**
+ * <p>Dimensions of the group of pixels which are under the same color filter.
+ * This specifies the width and height (pair of integers) of the group of pixels which fall
+ * under the same color filter for ULTRA_HIGH_RESOLUTION sensors.</p>
+ * <p>Sensors can have pixels grouped together under the same color filter in order
+ * to improve various aspects of imaging such as noise reduction, low light
+ * performance etc. These groups can be of various sizes such as 2X2 (quad bayer),
+ * 3X3 (nona-bayer). This key specifies the length and width of the pixels grouped under
+ * the same color filter.</p>
+ * <p>This key will not be present if REMOSAIC_REPROCESSING is not supported, since RAW images
+ * will have a regular bayer pattern.</p>
+ * <p>This key will not be present for sensors which don't have the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability.</p>
+ * <p><b>Units</b>: Pixels</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<android.util.Size> SENSOR_INFO_BINNING_FACTOR =
+ new Key<android.util.Size>("android.sensor.info.binningFactor", android.util.Size.class);
+
+ /**
* <p>The standard reference illuminant used as the scene light source when
* calculating the {@link CameraCharacteristics#SENSOR_COLOR_TRANSFORM1 android.sensor.colorTransform1},
* {@link CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM1 android.sensor.calibrationTransform1}, and
@@ -4150,6 +4434,111 @@
new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDynamicDepthStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
/**
+ * <p>The available depth dataspace stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream) when a CaptureRequest is submitted with
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to android.depth.availableDepthStreamConfigurations, for configurations which
+ * are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.depth.availableDepthStreamConfigurationsMaximumResolution", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for depth output formats when a CaptureRequest is submitted with
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to android.depth.availableDepthMinFrameDurations, for configurations which
+ * are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
+ * android.scaler.availableStallDurationsMaximumResolution for more details about
+ * calculating the max frame rate.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDepthMinFrameDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for depth streams for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to android.depth.availableDepthStallDurations, for configurations which
+ * are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDepthStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>The available dynamic depth dataspace stream
+ * configurations that this camera device supports (i.e. format, width, height,
+ * output/input stream) for CaptureRequests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to android.depth.availableDynamicDepthStreamConfigurations, for configurations
+ * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.depth.availableDynamicDepthStreamConfigurationsMaximumResolution", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for dynamic depth output streams for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to android.depth.availableDynamicDepthMinFrameDurations, for configurations
+ * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDynamicDepthMinFrameDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for dynamic depth streams for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Analogous to android.depth.availableDynamicDepthStallDurations, for configurations
+ * which are applicable when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDynamicDepthStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
* <p>String containing the ids of the underlying physical cameras.</p>
* <p>For a logical camera, this is concatenation of all underlying physical camera IDs.
* The null terminator for physical camera ID must be preserved so that the whole string
@@ -4286,6 +4675,47 @@
public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_STALL_DURATIONS =
new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+ /**
+ * <p>The available HEIC (ISO/IEC 23008-12) stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ * <p>Refer to android.heic.availableHeicStreamConfigurations for details.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.heic.availableHeicStreamConfigurationsMaximumResolution", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for HEIC output formats for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Refer to android.heic.availableHeicMinFrameDurations for details.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicMinFrameDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for HEIC streams for CaptureRequests where
+ * {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
+ * <p>Refer to android.heic.availableHeicStallDurations for details.</p>
+ * <p><b>Units</b>: (format, width, height, ns) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION =
+ new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b7b1a14..726bca4 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -507,6 +507,18 @@
return new CameraExtensionCharacteristics(mContext, cameraId, chars);
}
+ private Map<String, CameraCharacteristics> getPhysicalIdToCharsMap(
+ CameraCharacteristics chars) throws CameraAccessException {
+ HashMap<String, CameraCharacteristics> physicalIdsToChars =
+ new HashMap<String, CameraCharacteristics>();
+ Set<String> physicalCameraIds = chars.getPhysicalCameraIds();
+ for (String physicalCameraId : physicalCameraIds) {
+ CameraCharacteristics physicalChars = getCameraCharacteristics(physicalCameraId);
+ physicalIdsToChars.put(physicalCameraId, physicalChars);
+ }
+ return physicalIdsToChars;
+ }
+
/**
* Helper for opening a connection to a camera with the given ID.
*
@@ -535,17 +547,18 @@
throws CameraAccessException {
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
CameraDevice device = null;
-
+ Map<String, CameraCharacteristics> physicalIdsToChars =
+ getPhysicalIdToCharsMap(characteristics);
synchronized (mLock) {
ICameraDeviceUser cameraUser = null;
-
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
new android.hardware.camera2.impl.CameraDeviceImpl(
cameraId,
callback,
executor,
characteristics,
+ physicalIdsToChars,
mContext.getApplicationInfo().targetSdkVersion,
mContext);
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 924dcee..d4da3b9 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -586,7 +586,7 @@
* that is, {@link android.graphics.ImageFormat#PRIVATE } is included in the lists of
* formats returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats } and {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputFormats }.</li>
* <li>{@link android.hardware.camera2.params.StreamConfigurationMap#getValidOutputFormatsForInput }
- * returns non empty int[] for each supported input format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }.</li>
+ * returns non-empty int[] for each supported input format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }.</li>
* <li>Each size returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputSizes getInputSizes(ImageFormat.PRIVATE)} is also included in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes getOutputSizes(ImageFormat.PRIVATE)}</li>
* <li>Using {@link android.graphics.ImageFormat#PRIVATE } does not cause a frame rate drop
* relative to the sensor's maximum capture rate (at that resolution).</li>
@@ -1114,6 +1114,63 @@
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING = 15;
+ /**
+ * <p>This camera device is capable of producing ultra high resolution images in
+ * addition to the image sizes described in the
+ * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.
+ * It can operate in 'default' mode and 'max resolution' mode. It generally does this
+ * by binning pixels in 'default' mode and not binning them in 'max resolution' mode.
+ * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</code> describes the streams supported in 'default'
+ * mode.
+ * The stream configurations supported in 'max resolution' mode are described by
+ * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code>.</p>
+ *
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR = 16;
+
+ /**
+ * <p>The device supports reprocessing from the <code>RAW_SENSOR</code> format with a bayer pattern
+ * given by {@link CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR android.sensor.info.binningFactor} (m x n group of pixels with the same
+ * color filter) to a remosaiced regular bayer pattern.</p>
+ * <p>This capability will only be present for devices with
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability. When
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * devices do not advertise this capability,
+ * {@link android.graphics.ImageFormat#RAW_SENSOR } images will already have a
+ * regular bayer pattern.</p>
+ * <p>If a <code>RAW_SENSOR</code> stream is requested along with another non-RAW stream in a
+ * {@link android.hardware.camera2.CaptureRequest } (if multiple streams are supported
+ * when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }),
+ * the <code>RAW_SENSOR</code> stream will have a regular bayer pattern.</p>
+ * <p>This capability requires the camera device to support the following :
+ * * The {@link android.hardware.camera2.params.StreamConfigurationMap } mentioned below
+ * refers to the one, described by
+ * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code>.
+ * * One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.
+ * * {@link android.graphics.ImageFormat#RAW_SENSOR } is supported as an output/input
+ * format, that is, {@link android.graphics.ImageFormat#RAW_SENSOR } is included in the
+ * lists of formats returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats } and {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputFormats }.
+ * * {@link android.hardware.camera2.params.StreamConfigurationMap#getValidOutputFormatsForInput }
+ * returns non-empty int[] for each supported input format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }.
+ * * Each size returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputSizes getInputSizes(ImageFormat.RAW_SENSOR)} is also included in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes getOutputSizes(ImageFormat.RAW_SENSOR)}
+ * * Using {@link android.graphics.ImageFormat#RAW_SENSOR } does not cause a frame rate
+ * drop relative to the sensor's maximum capture rate (at that resolution).
+ * * No CaptureRequest controls will be applicable when a request has an input target
+ * with {@link android.graphics.ImageFormat#RAW_SENSOR } format.</p>
+ *
+ * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
+ * @see CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING = 17;
+
//
// Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
//
@@ -2955,6 +3012,27 @@
public static final int SENSOR_TEST_PATTERN_MODE_CUSTOM1 = 256;
//
+ // Enumeration values for CaptureRequest#SENSOR_PIXEL_MODE
+ //
+
+ /**
+ * <p>This is the default sensor pixel mode. This is the only sensor pixel mode
+ * supported unless a camera device advertises
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }.</p>
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ */
+ public static final int SENSOR_PIXEL_MODE_DEFAULT = 0;
+
+ /**
+ * <p>This sensor pixel mode is offered by devices with capability
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }.
+ * In this mode, sensors typically do not bin pixels, as a result can offer larger
+ * image sizes.</p>
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
+ */
+ public static final int SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION = 1;
+
+ //
// Enumeration values for CaptureRequest#SHADING_MODE
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 9ac2ff5..906256d 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1391,6 +1391,13 @@
* scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
* activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
* mode.</p>
+ * <p>For camera devices with the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+ * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
* <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
* distortion correction capability and mode</p>
@@ -1405,7 +1412,10 @@
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
*/
@PublicKey
@NonNull
@@ -1603,6 +1613,12 @@
* scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
* activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
* mode.</p>
+ * <p>For camera devices with the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+ * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
* <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
* distortion correction capability and mode</p>
@@ -1617,7 +1633,10 @@
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
*/
@PublicKey
@NonNull
@@ -1808,6 +1827,12 @@
* the scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
* activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
* mode.</p>
+ * <p>For camera devices with the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+ * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
* <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
* distortion correction capability and mode</p>
@@ -1822,7 +1847,10 @@
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
*/
@PublicKey
@NonNull
@@ -2896,6 +2924,12 @@
* coordinate system is post-zoom, meaning that the activeArraySize or
* preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
* {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
+ * <p>For camera devices with the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+ * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
* <p><b>Units</b>: Pixel coordinates relative to
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
@@ -2908,7 +2942,10 @@
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see CameraCharacteristics#SCALER_CROPPING_TYPE
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
*/
@PublicKey
@NonNull
@@ -3214,6 +3251,53 @@
new Key<Integer>("android.sensor.testPatternMode", int.class);
/**
+ * <p>Switches sensor pixel mode between maximum resolution mode and default mode.</p>
+ * <p>This key controls whether the camera sensor operates in
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+ * mode or not. By default, all camera devices operate in
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode.
+ * When operating in
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode, sensors
+ * with {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability would typically perform pixel binning in order to improve low light
+ * performance, noise reduction etc. However, in
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+ * mode (supported only
+ * by {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * sensors), sensors typically operate in unbinned mode allowing for a larger image size.
+ * The stream configurations supported in
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+ * mode are also different from those of
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode.
+ * They can be queried through
+ * {@link android.hardware.camera2.CameraCharacteristics#get } with
+ * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION) }.
+ * Unless reported by both
+ * {@link android.hardware.camera2.params.StreamConfigurationMap }s, the outputs from
+ * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code> and
+ * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</code>
+ * must not be mixed in the same CaptureRequest. In other words, these outputs are
+ * exclusive to each other.
+ * This key does not need to be set for reprocess requests.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #SENSOR_PIXEL_MODE_DEFAULT DEFAULT}</li>
+ * <li>{@link #SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION MAXIMUM_RESOLUTION}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
+ * @see #SENSOR_PIXEL_MODE_DEFAULT
+ * @see #SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<Integer> SENSOR_PIXEL_MODE =
+ new Key<Integer>("android.sensor.pixelMode", int.class);
+
+ /**
* <p>Quality of lens shading correction applied
* to the image data.</p>
* <p>When set to OFF mode, no lens shading correction will be applied by the
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index e7457e7..6ff68c1 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -812,6 +812,13 @@
* scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
* activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
* mode.</p>
+ * <p>For camera devices with the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+ * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
* <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
* distortion correction capability and mode</p>
@@ -826,7 +833,10 @@
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
*/
@PublicKey
@NonNull
@@ -1274,6 +1284,12 @@
* scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
* activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
* mode.</p>
+ * <p>For camera devices with the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+ * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
* <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
* distortion correction capability and mode</p>
@@ -1288,7 +1304,10 @@
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
*/
@PublicKey
@NonNull
@@ -1890,6 +1909,12 @@
* the scene as they do before. See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details. Whether to use
* activeArraySize or preCorrectionActiveArraySize still depends on distortion correction
* mode.</p>
+ * <p>For camera devices with the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+ * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
* <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
* distortion correction capability and mode</p>
@@ -1904,7 +1929,10 @@
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
*/
@PublicKey
@NonNull
@@ -3565,6 +3593,12 @@
* coordinate system is post-zoom, meaning that the activeArraySize or
* preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
* {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
+ * <p>For camera devices with the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability, {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} /
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the
+ * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p>
* <p><b>Units</b>: Pixel coordinates relative to
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
@@ -3577,7 +3611,10 @@
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see CameraCharacteristics#SCALER_CROPPING_TYPE
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION
+ * @see CaptureRequest#SENSOR_PIXEL_MODE
*/
@PublicKey
@NonNull
@@ -4107,6 +4144,69 @@
new Key<Integer>("android.sensor.dynamicWhiteLevel", int.class);
/**
+ * <p>Switches sensor pixel mode between maximum resolution mode and default mode.</p>
+ * <p>This key controls whether the camera sensor operates in
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+ * mode or not. By default, all camera devices operate in
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode.
+ * When operating in
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode, sensors
+ * with {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability would typically perform pixel binning in order to improve low light
+ * performance, noise reduction etc. However, in
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+ * mode (supported only
+ * by {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * sensors), sensors typically operate in unbinned mode allowing for a larger image size.
+ * The stream configurations supported in
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }
+ * mode are also different from those of
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT } mode.
+ * They can be queried through
+ * {@link android.hardware.camera2.CameraCharacteristics#get } with
+ * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION) }.
+ * Unless reported by both
+ * {@link android.hardware.camera2.params.StreamConfigurationMap }s, the outputs from
+ * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code> and
+ * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</code>
+ * must not be mixed in the same CaptureRequest. In other words, these outputs are
+ * exclusive to each other.
+ * This key does not need to be set for reprocess requests.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #SENSOR_PIXEL_MODE_DEFAULT DEFAULT}</li>
+ * <li>{@link #SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION MAXIMUM_RESOLUTION}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
+ * @see #SENSOR_PIXEL_MODE_DEFAULT
+ * @see #SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<Integer> SENSOR_PIXEL_MODE =
+ new Key<Integer>("android.sensor.pixelMode", int.class);
+
+ /**
+ * <p>Whether <code>RAW</code> images requested have their bayer pattern as described by
+ * {@link CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR android.sensor.info.binningFactor}.</p>
+ * <p>This key will only be present in devices advertisting the
+ * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
+ * capability which also advertise <code>REMOSAIC_REPROCESSING</code> capability. On all other devices
+ * RAW targets will have a regular bayer pattern.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_BINNING_FACTOR
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<Boolean> SENSOR_RAW_BINNING_FACTOR_USED =
+ new Key<Boolean>("android.sensor.rawBinningFactorUsed", boolean.class);
+
+ /**
* <p>Quality of lens shading correction applied
* to the image data.</p>
* <p>When set to OFF mode, no lens shading correction will be applied by the
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 0a42981..2920e67 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -20,6 +20,7 @@
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CameraOfflineSession;
import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
import android.hardware.camera2.CaptureRequest;
@@ -81,11 +82,17 @@
if (request == null) {
throw new IllegalArgumentException("Input capture request must not be null");
}
+ CameraCharacteristics.Key<StreamConfigurationMap> ck =
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
+ Integer sensorPixelMode = request.get(CaptureRequest.SENSOR_PIXEL_MODE);
+ if (sensorPixelMode != null && sensorPixelMode ==
+ CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+ ck = CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
+ }
Collection<Surface> outputSurfaces = request.getTargets();
Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
- StreamConfigurationMap config =
- mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ StreamConfigurationMap config = mCharacteristics.get(ck);
SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange, config);
// Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 4defd23..b578bf8 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.graphics.ImageFormat;
import android.hardware.ICameraService;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
@@ -62,6 +63,8 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -114,6 +117,7 @@
private final String mCameraId;
private final CameraCharacteristics mCharacteristics;
+ private final Map<String, CameraCharacteristics> mPhysicalIdsToChars;
private final int mTotalPartialCount;
private final Context mContext;
@@ -257,7 +261,9 @@
};
public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor,
- CameraCharacteristics characteristics, int appTargetSdkVersion,
+ CameraCharacteristics characteristics,
+ Map<String, CameraCharacteristics> physicalIdsToChars,
+ int appTargetSdkVersion,
Context ctx) {
if (cameraId == null || callback == null || executor == null || characteristics == null) {
throw new IllegalArgumentException("Null argument given");
@@ -266,6 +272,7 @@
mDeviceCallback = callback;
mDeviceExecutor = executor;
mCharacteristics = characteristics;
+ mPhysicalIdsToChars = physicalIdsToChars;
mAppTargetSdkVersion = appTargetSdkVersion;
mContext = ctx;
@@ -1357,11 +1364,71 @@
}
}
+ private boolean checkInputConfigurationWithStreamConfigurationsAs(
+ InputConfiguration inputConfig, StreamConfigurationMap configMap) {
+ int[] inputFormats = configMap.getInputFormats();
+ boolean validFormat = false;
+ int inputFormat = inputConfig.getFormat();
+ for (int format : inputFormats) {
+ if (format == inputFormat) {
+ validFormat = true;
+ }
+ }
+
+ if (validFormat == false) {
+ return false;
+ }
+
+ boolean validSize = false;
+ Size[] inputSizes = configMap.getInputSizes(inputFormat);
+ for (Size s : inputSizes) {
+ if (inputConfig.getWidth() == s.getWidth() &&
+ inputConfig.getHeight() == s.getHeight()) {
+ validSize = true;
+ }
+ }
+
+ if (validSize == false) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkInputConfigurationWithStreamConfigurations(
+ InputConfiguration inputConfig, boolean maxResolution) {
+ // Check if either this logical camera or any of its physical cameras support the
+ // input config. If they do, the input config is valid.
+ CameraCharacteristics.Key<StreamConfigurationMap> ck =
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
+
+ if (maxResolution) {
+ ck = CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
+ }
+
+ StreamConfigurationMap configMap = mCharacteristics.get(ck);
+
+ if (configMap != null &&
+ checkInputConfigurationWithStreamConfigurationsAs(inputConfig, configMap)) {
+ return true;
+ }
+
+ for (Map.Entry<String, CameraCharacteristics> entry : mPhysicalIdsToChars.entrySet()) {
+ configMap = entry.getValue().get(ck);
+
+ if (configMap != null &&
+ checkInputConfigurationWithStreamConfigurationsAs(inputConfig, configMap)) {
+ // Input config supported.
+ return true;
+ }
+ }
+ return false;
+ }
+
private void checkInputConfiguration(InputConfiguration inputConfig) {
if (inputConfig == null) {
return;
}
-
+ int inputFormat = inputConfig.getFormat();
if (inputConfig.isMultiResolution()) {
MultiResolutionStreamConfigurationMap configMap = mCharacteristics.get(
CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP);
@@ -1369,19 +1436,19 @@
int[] inputFormats = configMap.getInputFormats();
boolean validFormat = false;
for (int format : inputFormats) {
- if (format == inputConfig.getFormat()) {
+ if (format == inputFormat) {
validFormat = true;
}
}
if (validFormat == false) {
throw new IllegalArgumentException("multi-resolution input format " +
- inputConfig.getFormat() + " is not valid");
+ inputFormat + " is not valid");
}
boolean validSize = false;
Collection<MultiResolutionStreamInfo> inputStreamInfo =
- configMap.getInputInfo(inputConfig.getFormat());
+ configMap.getInputInfo(inputFormat);
for (MultiResolutionStreamInfo info : inputStreamInfo) {
if (inputConfig.getWidth() == info.getWidth() &&
inputConfig.getHeight() == info.getHeight()) {
@@ -1394,34 +1461,11 @@
inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid");
}
} else {
- StreamConfigurationMap configMap = mCharacteristics.get(
- CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-
- int[] inputFormats = configMap.getInputFormats();
- boolean validFormat = false;
- for (int format : inputFormats) {
- if (format == inputConfig.getFormat()) {
- validFormat = true;
- }
- }
-
- if (validFormat == false) {
- throw new IllegalArgumentException("input format " + inputConfig.getFormat() +
- " is not valid");
- }
-
- boolean validSize = false;
- Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat());
- for (Size s : inputSizes) {
- if (inputConfig.getWidth() == s.getWidth() &&
- inputConfig.getHeight() == s.getHeight()) {
- validSize = true;
- }
- }
-
- if (validSize == false) {
- throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" +
- inputConfig.getHeight() + " is not valid");
+ if (!checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/false) &&
+ !checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/true)) {
+ throw new IllegalArgumentException("Input config with format " +
+ inputFormat + " and size " + inputConfig.getWidth() + "x" +
+ inputConfig.getHeight() + " not supported by camera id " + mCameraId);
}
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 0cdf744..aa84b02 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -327,6 +327,10 @@
private static final String GPS_PROCESS = "GPS";
private static final int FACE_LANDMARK_SIZE = 6;
+ private static final int MANDATORY_STREAM_CONFIGURATIONS_DEFAULT = 0;
+ private static final int MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION = 1;
+ private static final int MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT = 2;
+
private static String translateLocationProviderToProcess(final String provider) {
if (provider == null) {
return null;
@@ -644,6 +648,15 @@
return (T) metadata.getStreamConfigurationMap();
}
});
+ sGetCommandMap.put(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getStreamConfigurationMapMaximumResolution();
+ }
+ });
sGetCommandMap.put(
CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS.getNativeKey(),
new GetCommand() {
@@ -664,6 +677,16 @@
});
sGetCommandMap.put(
+ CameraCharacteristics.SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getMandatoryMaximumResolutionStreamCombinations();
+ }
+ });
+
+ sGetCommandMap.put(
CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
@Override
@SuppressWarnings("unchecked")
@@ -1285,12 +1308,12 @@
return recommendedConfigurations;
}
- private boolean isBurstSupported() {
+ private boolean isCapabilitySupported(int capabilityRequested) {
boolean ret = false;
int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
for (int capability : capabilities) {
- if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE) {
+ if (capabilityRequested == capability) {
ret = true;
break;
}
@@ -1299,8 +1322,18 @@
return ret;
}
+ private boolean isUltraHighResolutionSensor() {
+ return isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR);
+
+ }
+ private boolean isBurstSupported() {
+ return isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
+ }
+
private MandatoryStreamCombination[] getMandatoryStreamCombinationsHelper(
- boolean getConcurrent) {
+ int mandatoryStreamsType) {
int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
ArrayList<Integer> caps = new ArrayList<Integer>();
caps.ensureCapacity(capabilities.length);
@@ -1309,20 +1342,25 @@
}
int hwLevel = getBase(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
MandatoryStreamCombination.Builder build = new MandatoryStreamCombination.Builder(
- mCameraId, hwLevel, mDisplaySize, caps, getStreamConfigurationMap());
+ mCameraId, hwLevel, mDisplaySize, caps, getStreamConfigurationMap(),
+ getStreamConfigurationMapMaximumResolution());
List<MandatoryStreamCombination> combs = null;
- if (getConcurrent) {
- combs = build.getAvailableMandatoryConcurrentStreamCombinations();
- } else {
- combs = build.getAvailableMandatoryStreamCombinations();
+ switch (mandatoryStreamsType) {
+ case MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT:
+ combs = build.getAvailableMandatoryConcurrentStreamCombinations();
+ break;
+ case MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION:
+ combs = build.getAvailableMandatoryMaximumResolutionStreamCombinations();
+ break;
+ default:
+ combs = build.getAvailableMandatoryStreamCombinations();
}
if ((combs != null) && (!combs.isEmpty())) {
MandatoryStreamCombination[] combArray = new MandatoryStreamCombination[combs.size()];
combArray = combs.toArray(combArray);
return combArray;
}
-
return null;
}
@@ -1330,11 +1368,18 @@
if (!mHasMandatoryConcurrentStreams) {
return null;
}
- return getMandatoryStreamCombinationsHelper(true);
+ return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT);
+ }
+
+ private MandatoryStreamCombination[] getMandatoryMaximumResolutionStreamCombinations() {
+ if (!isUltraHighResolutionSensor()) {
+ return null;
+ }
+ return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION);
}
private MandatoryStreamCombination[] getMandatoryStreamCombinations() {
- return getMandatoryStreamCombinationsHelper(false);
+ return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_DEFAULT);
}
private StreamConfigurationMap getStreamConfigurationMap() {
@@ -1377,6 +1422,50 @@
listHighResolution);
}
+ private StreamConfigurationMap getStreamConfigurationMapMaximumResolution() {
+ if (!isUltraHighResolutionSensor()) {
+ return null;
+ }
+ StreamConfiguration[] configurations = getBase(
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfigurationDuration[] minFrameDurations = getBase(
+ CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfigurationDuration[] stallDurations = getBase(
+ CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfiguration[] depthConfigurations = getBase(
+ CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfigurationDuration[] depthMinFrameDurations = getBase(
+ CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfigurationDuration[] depthStallDurations = getBase(
+ CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfiguration[] dynamicDepthConfigurations = getBase(
+ CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfigurationDuration[] dynamicDepthMinFrameDurations = getBase(
+ CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfigurationDuration[] dynamicDepthStallDurations = getBase(
+ CameraCharacteristics.DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfiguration[] heicConfigurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfigurationDuration[] heicMinFrameDurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION);
+ StreamConfigurationDuration[] heicStallDurations = getBase(
+ CameraCharacteristics.HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION);
+ HighSpeedVideoConfiguration[] highSpeedVideoConfigurations = getBase(
+ CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS_MAXIMUM_RESOLUTION);
+ ReprocessFormatsMap inputOutputFormatsMap = getBase(
+ CameraCharacteristics.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP_MAXIMUM_RESOLUTION);
+ // TODO: Is this correct, burst capability shouldn't necessarily correspond to max res mode
+ boolean listHighResolution = isBurstSupported();
+ return new StreamConfigurationMap(
+ configurations, minFrameDurations, stallDurations,
+ depthConfigurations, depthMinFrameDurations, depthStallDurations,
+ dynamicDepthConfigurations, dynamicDepthMinFrameDurations,
+ dynamicDepthStallDurations, heicConfigurations,
+ heicMinFrameDurations, heicStallDurations,
+ highSpeedVideoConfigurations, inputOutputFormatsMap,
+ listHighResolution, false);
+ }
+
private <T> Integer getMaxRegions(Key<T> key) {
final int AE = 0;
final int AWB = 1;
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 8a0172e..29bdee3 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -62,6 +62,7 @@
private final int mFormat;
private final ArrayList<Size> mAvailableSizes = new ArrayList<Size> ();
private final boolean mIsInput;
+ private final boolean mIsUltraHighResolution;
/**
* Create a new {@link MandatoryStreamInformation}.
@@ -74,8 +75,8 @@
* ImageFormat/PixelFormat.
* @hide
*/
- public MandatoryStreamInformation(@NonNull List<Size> availableSizes, int format) {
- this(availableSizes, format, /*isInput*/false);
+ public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format) {
+ this(availableSizes, format, /*isInput*/false, /*maximumResolution*/false);
}
/**
@@ -92,12 +93,31 @@
*/
public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
boolean isInput) {
+ this(availableSizes, format, isInput, /*maximumResolution*/ false);
+ }
+
+ /**
+ * Create a new {@link MandatoryStreamInformation}.
+ *
+ @param availableSizes List of possible stream sizes.
+ * @param format Image format.
+ * @param isInput Flag indicating whether this stream is input.
+ * @param isMaxResolution Flag indicating whether this is a maximum resolution stream.
+ *
+ * @throws IllegalArgumentException
+ * if sizes is empty or if the format was not user-defined in
+ * ImageFormat/PixelFormat.
+ * @hide
+ */
+ public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
+ boolean isInput, boolean isUltraHighResolution) {
if (availableSizes.isEmpty()) {
throw new IllegalArgumentException("No available sizes");
}
mAvailableSizes.addAll(availableSizes);
mFormat = checkArgumentFormat(format);
mIsInput = isInput;
+ mIsUltraHighResolution = isUltraHighResolution;
}
/**
@@ -109,6 +129,18 @@
}
/**
+ * Confirms whether or not this is an ultra high resolution stream.
+ * An 'ultra high resolution' stream is one which has a configuration which appears in
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
+ * Streams which are ultra high resolution must not be included with streams which are not
+ * ultra high resolution in the same {@link android.hardware.camera2.CaptureRequest}.
+ * @return true in case the stream is ultra high resolution, false otherwise.
+ */
+ public boolean isUltraHighResolution() {
+ return mIsUltraHighResolution;
+ }
+
+ /**
* Return the list of available sizes for this mandatory stream.
*
* <p>Per documented {@link CameraDevice#createCaptureSession guideline} the largest
@@ -153,6 +185,7 @@
if (obj instanceof MandatoryStreamInformation) {
final MandatoryStreamInformation other = (MandatoryStreamInformation) obj;
if ((mFormat != other.mFormat) || (mIsInput != other.mIsInput) ||
+ (mIsUltraHighResolution != other.mIsUltraHighResolution) ||
(mAvailableSizes.size() != other.mAvailableSizes.size())) {
return false;
}
@@ -169,7 +202,7 @@
@Override
public int hashCode() {
return HashCodeHelpers.hashCode(mFormat, Boolean.hashCode(mIsInput),
- mAvailableSizes.hashCode());
+ Boolean.hashCode(mIsUltraHighResolution), mAvailableSizes.hashCode());
}
}
@@ -267,21 +300,26 @@
mStreamsInformation.hashCode());
}
- private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p, s1440p }
- private static enum ReprocessType { NONE, PRIVATE, YUV }
+ private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p, s1440p, FULL_RES }
+ private static enum ReprocessType { NONE, PRIVATE, YUV, REMOSAIC }
private static final class StreamTemplate {
public int mFormat;
public SizeThreshold mSizeThreshold;
public boolean mIsInput;
+ public boolean mIsUltraHighResolution;
public StreamTemplate(int format, SizeThreshold sizeThreshold) {
- this(format, sizeThreshold, /*isInput*/false);
+ this(format, sizeThreshold, /*isInput*/false, /*ultraHighResolution*/false);
}
-
public StreamTemplate(@Format int format, @NonNull SizeThreshold sizeThreshold,
boolean isInput) {
+ this(format, sizeThreshold, isInput, /*ultraHighResolution*/ false);
+ }
+ public StreamTemplate(@Format int format, @NonNull SizeThreshold sizeThreshold,
+ boolean isInput, boolean isUltraHighResolution) {
mFormat = format;
mSizeThreshold = sizeThreshold;
mIsInput = isInput;
+ mIsUltraHighResolution = isUltraHighResolution;
}
}
@@ -691,6 +729,18 @@
"Depth capture for mesh based object rendering"),
};
+ private static StreamCombinationTemplate sUltraHighResolutionStreamCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.FULL_RES) },
+ "Full res YUV image capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.RAW_SENSOR, SizeThreshold.FULL_RES) },
+ "Full res RAW capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.FULL_RES) },
+ "Full res JPEG still image capture"),
+ };
+
/**
* Helper builder class to generate a list of available mandatory stream combinations.
* @hide
@@ -700,6 +750,7 @@
private List<Integer> mCapabilities;
private int mHwLevel, mCameraId;
private StreamConfigurationMap mStreamConfigMap;
+ private StreamConfigurationMap mStreamConfigMapMaximumResolution;
private boolean mIsHiddenPhysicalCamera;
private final Size kPreviewSizeBound = new Size(1920, 1088);
@@ -712,13 +763,17 @@
* @param displaySize The device display size.
* @param capabilities The camera device capabilities.
* @param sm The camera device stream configuration map.
+ * @param smMaxResolution The camera device stream configuration map when it runs in max
+ * resolution mode.
*/
public Builder(int cameraId, int hwLevel, @NonNull Size displaySize,
- @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm) {
+ @NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm,
+ StreamConfigurationMap smMaxResolution) {
mCameraId = cameraId;
mDisplaySize = displaySize;
mCapabilities = capabilities;
mStreamConfigMap = sm;
+ mStreamConfigMapMaximumResolution = smMaxResolution;
mHwLevel = hwLevel;
mIsHiddenPhysicalCamera =
CameraManager.isHiddenPhysicalCamera(Integer.toString(mCameraId));
@@ -797,6 +852,98 @@
}
/**
+ * Retrieve a list of all available mandatory stream combinations supported when
+ * {@link CaptureRequest#ANDROID_SENSOR_PIXEL_MODE} is set to
+ * {@link CameraMetadata#ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION}.
+ *
+ * @return a non-modifiable list of supported mandatory stream combinations or
+ * null in case device is not backward compatible or the method encounters
+ * an error.
+ */
+ public @NonNull List<MandatoryStreamCombination>
+ getAvailableMandatoryMaximumResolutionStreamCombinations() {
+
+ ArrayList<StreamCombinationTemplate> chosenStreamCombinations =
+ new ArrayList<StreamCombinationTemplate>();
+
+ chosenStreamCombinations.addAll(Arrays.asList(sUltraHighResolutionStreamCombinations));
+
+ ArrayList<MandatoryStreamCombination> availableStreamCombinations =
+ new ArrayList<MandatoryStreamCombination>();
+ boolean addRemosaicReprocessing = isRemosaicReprocessingSupported();
+ int remosaicSize = 0;
+ if (addRemosaicReprocessing) {
+ remosaicSize = 1;
+ }
+ availableStreamCombinations.ensureCapacity(
+ chosenStreamCombinations.size() + remosaicSize);
+ fillMandatoryOutputStreamCombinations(availableStreamCombinations,
+ chosenStreamCombinations, mStreamConfigMapMaximumResolution);
+ if (isRemosaicReprocessingSupported()) {
+ // Add reprocess mandatory streams
+ ArrayList<MandatoryStreamInformation> streamsInfo =
+ new ArrayList<MandatoryStreamInformation>();
+
+ ArrayList<Size> inputSize = new ArrayList<Size>();
+ Size maxRawInputSize = getMaxSize(mStreamConfigMapMaximumResolution.getInputSizes(
+ ImageFormat.RAW_SENSOR));
+ inputSize.add(maxRawInputSize);
+
+ streamsInfo.add(new MandatoryStreamInformation(inputSize,
+ ImageFormat.RAW_SENSOR, /*isInput*/true, /*ultraHighResolution*/true));
+ streamsInfo.add(new MandatoryStreamInformation(inputSize,
+ ImageFormat.RAW_SENSOR, /*isInput*/ false, /*ultraHighResolution*/true));
+ MandatoryStreamCombination streamCombination;
+ streamCombination = new MandatoryStreamCombination(streamsInfo,
+ "Remosaic reprocessing", /*isReprocess*/true);
+ availableStreamCombinations.add(streamCombination);
+ }
+ return Collections.unmodifiableList(availableStreamCombinations);
+ }
+
+ private void fillMandatoryOutputStreamCombinations(
+ ArrayList<MandatoryStreamCombination> availableStreamCombinations,
+ ArrayList<StreamCombinationTemplate> chosenStreamCombinations,
+ StreamConfigurationMap streamConfigMap) {
+
+ for (StreamCombinationTemplate combTemplate : chosenStreamCombinations) {
+ ArrayList<MandatoryStreamInformation> streamsInfo =
+ new ArrayList<MandatoryStreamInformation>();
+ streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
+
+ for (StreamTemplate template : combTemplate.mStreamTemplates) {
+ MandatoryStreamInformation streamInfo;
+ List<Size> sizes = new ArrayList<Size>();
+ Size sizeChosen =
+ getMaxSize(streamConfigMap.getOutputSizes(
+ template.mFormat));
+ sizes.add(sizeChosen);
+ try {
+ streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
+ /*isInput*/ false, /*ultraHighResolution*/ true);
+ } catch (IllegalArgumentException e) {
+ String cause = "No available sizes found for format: " + template.mFormat
+ + " size threshold: " + template.mSizeThreshold + " combination: "
+ + combTemplate.mDescription;
+ throw new RuntimeException(cause, e);
+ }
+ streamsInfo.add(streamInfo);
+ }
+
+ MandatoryStreamCombination streamCombination;
+ try {
+ streamCombination = new MandatoryStreamCombination(streamsInfo,
+ combTemplate.mDescription, /*isReprocess*/false);
+ } catch (IllegalArgumentException e) {
+ String cause = "No stream information for mandatory combination: "
+ + combTemplate.mDescription;
+ throw new RuntimeException(cause, e);
+ }
+ availableStreamCombinations.add(streamCombination);
+ }
+ }
+
+ /**
* Retrieve a list of all available mandatory stream combinations.
*
* @return a non-modifiable list of supported mandatory stream combinations or
@@ -948,7 +1095,6 @@
inputSize.add(maxYUVInputSize);
format = ImageFormat.YUV_420_888;
}
-
streamsInfo.add(new MandatoryStreamInformation(inputSize, format,
/*isInput*/true));
streamsInfo.add(new MandatoryStreamInformation(inputSize, format));
@@ -974,7 +1120,6 @@
combTemplate.mDescription);
return null;
}
-
streamsInfo.add(streamInfo);
}
@@ -1220,6 +1365,14 @@
}
/**
+ * Check whether the current device supports YUV reprocessing.
+ */
+ private boolean isRemosaicReprocessingSupported() {
+ return isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING);
+ }
+
+ /**
* Return the maximum supported video size using the camcorder profile information.
*
* @return Maximum supported video size.
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index e31bd60..84736dc 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -19,6 +19,7 @@
import static com.android.internal.util.Preconditions.*;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -26,6 +27,7 @@
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.MultiResolutionImageReader;
import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.hardware.camera2.utils.HashCodeHelpers;
@@ -33,10 +35,13 @@
import android.media.ImageReader;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -145,6 +150,13 @@
*/
public static final int SURFACE_GROUP_ID_NONE = -1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SENSOR_PIXEL_MODE_"}, value =
+ {CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT,
+ CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION})
+ public @interface SensorPixelMode {};
+
/**
* Create a new {@link OutputConfiguration} instance with a {@link Surface}.
*
@@ -306,6 +318,7 @@
mIsShared = false;
mPhysicalCameraId = null;
mIsMultiResolution = false;
+ mSensorPixelModesUsed = new ArrayList<Integer>();
}
/**
@@ -399,6 +412,7 @@
mIsShared = false;
mPhysicalCameraId = null;
mIsMultiResolution = false;
+ mSensorPixelModesUsed = new ArrayList<Integer>();
}
/**
@@ -485,6 +499,81 @@
}
/**
+ * Add a sensor pixel mode that this OutputConfiguration will be used in.
+ *
+ * <p> In the case that this output stream configuration (format, width, height) is
+ * available through {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
+ * configurations and
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
+ * configurations, the camera sub-system will assume that this {@link OutputConfiguration} will
+ * be used only with {@link android.hardware.camera2.CaptureRequest}s which has
+ * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT}.
+ * In such cases, if clients intend to use the
+ * {@link OutputConfiguration}(s) in a {@link android.hardware.camera2.CaptureRequest} with
+ * other sensor pixel modes, they must specify which
+ * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE}(s) they will use this
+ * {@link OutputConfiguration} with, by calling this method.
+ *
+ * In case this output stream configuration (format, width, height) is only in
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION},
+ * configurations, this output target must only be used with
+ * {@link android.hardware.camera2.CaptureRequest}s which has
+ * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION} and that
+ * is what the camera sub-system will assume. If clients add
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT} in this
+ * case, session configuration will fail, if this {@link OutputConfiguration} is included.
+ *
+ * In case this output stream configuration (format, width, height) is only in
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP},
+ * configurations, this output target must only be used with
+ * {@link android.hardware.camera2.CaptureRequest}s which has
+ * {@link android.hardware.camera2.CaptureRequest#SENSOR_PIXEL_MODE} set to
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_DEFAULT} and that is what
+ * the camera sub-system will assume. If clients add
+ * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION} in this
+ * case, session configuration will fail, if this {@link OutputConfiguration} is included.
+ *
+ * @param sensorPixelModeUsed The sensor pixel mode this OutputConfiguration will be used with
+ * </p>
+ *
+ */
+ public void addSensorPixelModeUsed(@SensorPixelMode int sensorPixelModeUsed) {
+ // Verify that the values are in range.
+ if (sensorPixelModeUsed != CameraMetadata.SENSOR_PIXEL_MODE_DEFAULT &&
+ sensorPixelModeUsed != CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+ throw new IllegalArgumentException("Not a valid sensor pixel mode " +
+ sensorPixelModeUsed);
+ }
+
+ if (mSensorPixelModesUsed.contains(sensorPixelModeUsed)) {
+ // Already added, ignore;
+ return;
+ }
+ mSensorPixelModesUsed.add(sensorPixelModeUsed);
+ }
+
+ /**
+ * Remove a sensor pixel mode, previously added through addSensorPixelModeUsed, from this
+ * OutputConfiguration.
+ *
+ * <p> Sensor pixel modes added via calls to {@link #addSensorPixelModeUsed} can also be removed
+ * from the OutputConfiguration.</p>
+ *
+ * @param sensorPixelModeUsed The sensor pixel mode to be removed.
+ *
+ * @throws IllegalArgumentException If the sensor pixel mode wasn't previously added
+ * through {@link #addSensorPixelModeUsed}.
+ */
+ public void removeSensorPixelModeUsed(@SensorPixelMode int sensorPixelModeUsed) {
+ if (!mSensorPixelModesUsed.remove(Integer.valueOf(sensorPixelModeUsed))) {
+ throw new IllegalArgumentException("sensorPixelMode " + sensorPixelModeUsed +
+ "is not part of this output configuration");
+ }
+ }
+
+ /**
* Check if this configuration is for a physical camera.
*
* <p>This returns true if the output configuration was for a physical camera making up a
@@ -625,6 +714,7 @@
this.mIsShared = other.mIsShared;
this.mPhysicalCameraId = other.mPhysicalCameraId;
this.mIsMultiResolution = other.mIsMultiResolution;
+ this.mSensorPixelModesUsed = other.mSensorPixelModesUsed;
}
/**
@@ -642,7 +732,8 @@
source.readTypedList(surfaces, Surface.CREATOR);
String physicalCameraId = source.readString();
boolean isMultiResolutionOutput = source.readInt() == 1;
-
+ ArrayList<Integer> sensorPixelModesUsed = new ArrayList<Integer>();
+ source.readList(sensorPixelModesUsed, Integer.class.getClassLoader());
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
mSurfaceGroupId = surfaceSetId;
@@ -666,6 +757,7 @@
}
mPhysicalCameraId = physicalCameraId;
mIsMultiResolution = isMultiResolutionOutput;
+ mSensorPixelModesUsed = sensorPixelModesUsed;
}
/**
@@ -766,6 +858,7 @@
dest.writeTypedList(mSurfaces);
dest.writeString(mPhysicalCameraId);
dest.writeInt(mIsMultiResolution ? 1 : 0);
+ dest.writeList(mSensorPixelModesUsed);
}
/**
@@ -798,7 +891,14 @@
!Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
mIsMultiResolution != other.mIsMultiResolution)
return false;
-
+ if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
+ return false;
+ }
+ for (int j = 0; j < mSensorPixelModesUsed.size(); j++) {
+ if (mSensorPixelModesUsed.get(j) != other.mSensorPixelModesUsed.get(j)) {
+ return false;
+ }
+ }
int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
for (int i = 0; i < minLen; i++) {
if (mSurfaces.get(i) != other.mSurfaces.get(i))
@@ -823,7 +923,7 @@
mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
- mIsMultiResolution ? 1 : 0);
+ mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode());
}
return HashCodeHelpers.hashCode(
@@ -831,7 +931,7 @@
mConfiguredSize.hashCode(), mConfiguredFormat,
mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
- mIsMultiResolution ? 1 : 0);
+ mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode());
}
private static final String TAG = "OutputConfiguration";
@@ -861,4 +961,6 @@
// Flag indicating if this config is for a multi-resolution output with a
// MultiResolutionImageReader
private boolean mIsMultiResolution;
+ // The sensor pixel modes that this OutputConfiguration will use
+ private ArrayList<Integer> mSensorPixelModesUsed;
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 6dd6744..6c2d140 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -17,8 +17,10 @@
package android.hardware.display;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.HdrCapabilities.HdrType;
import android.Manifest;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.LongDef;
import android.annotation.NonNull;
@@ -36,6 +38,7 @@
import android.os.Build;
import android.os.Handler;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.Surface;
@@ -346,6 +349,37 @@
/** @hide */
+ @IntDef(prefix = {"MATCH_CONTENT_FRAMERATE_"}, value = {
+ MATCH_CONTENT_FRAMERATE_UNKNOWN,
+ MATCH_CONTENT_FRAMERATE_NEVER,
+ MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY,
+ MATCH_CONTENT_FRAMERATE_ALWAYS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MatchContentFrameRateType {}
+
+ /**
+ * Match content frame rate user preference is unknown.
+ */
+ public static final int MATCH_CONTENT_FRAMERATE_UNKNOWN = -1;
+
+ /**
+ * No mode switching is allowed.
+ */
+ public static final int MATCH_CONTENT_FRAMERATE_NEVER = 0;
+
+ /**
+ * Only refresh rate switches without visual interruptions are allowed.
+ */
+ public static final int MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY = 1;
+
+ /**
+ * Refresh rate switches between all refresh rates are allowed even if they have visual
+ * interruptions for the user.
+ */
+ public static final int MATCH_CONTENT_FRAMERATE_ALWAYS = 2;
+
+ /** @hide */
@IntDef(prefix = {"SWITCHING_TYPE_"}, value = {
SWITCHING_TYPE_NONE,
SWITCHING_TYPE_WITHIN_GROUPS,
@@ -709,6 +743,55 @@
}
/**
+ * Sets the HDR types that have been disabled by user.
+ * @param userDisabledTypes the HDR types to disable.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public void setUserDisabledHdrTypes(@NonNull @HdrType int[] userDisabledTypes) {
+ mGlobal.setUserDisabledHdrTypes(userDisabledTypes);
+ }
+
+ /**
+ * Sets whether or not the user disabled HDR types are returned from
+ * {@link Display#getHdrCapabilities}.
+ *
+ * @param areUserDisabledHdrTypesAllowed If true, the user-disabled types
+ * are ignored and returned, if the display supports them. If false, the
+ * user-disabled types are taken into consideration and are never returned,
+ * even if the display supports them.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed) {
+ mGlobal.setAreUserDisabledHdrTypesAllowed(areUserDisabledHdrTypesAllowed);
+ }
+
+ /**
+ * Returns whether or not the user-disabled HDR types are returned from
+ * {@link Display#getHdrCapabilities}.
+ *
+ * @hide
+ */
+ @TestApi
+ public boolean areUserDisabledHdrTypesAllowed() {
+ return mGlobal.areUserDisabledHdrTypesAllowed();
+ }
+
+ /**
+ * Returns the HDR formats disabled by the user.
+ *
+ * @hide
+ */
+ @TestApi
+ public @NonNull int[] getUserDisabledHdrTypes() {
+ return mGlobal.getUserDisabledHdrTypes();
+ }
+
+
+ /**
* Creates a virtual display.
*
* @see #createVirtualDisplay(String, int, int, int, Surface, int,
@@ -921,6 +1004,43 @@
mGlobal.setTemporaryBrightness(displayId, brightness);
}
+
+ /**
+ * Sets the brightness of the specified display.
+ * <p>
+ * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS}
+ * permission.
+ * </p>
+ *
+ * @param displayId the logical display id
+ * @param brightness The brightness value from 0.0f to 1.0f.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ public void setBrightness(int displayId, @FloatRange(from = 0f, to = 1f) float brightness) {
+ mGlobal.setBrightness(displayId, brightness);
+ }
+
+
+ /**
+ * Gets the brightness of the specified display.
+ * <p>
+ * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS}
+ * permission.
+ * </p>
+ *
+ * @param displayId The display of which brightness value to get from.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @FloatRange(from = 0f, to = 1f)
+ public float getBrightness(int displayId) {
+ return mGlobal.getBrightness(displayId);
+ }
+
+
/**
* Temporarily sets the auto brightness adjustment factor.
* <p>
@@ -988,14 +1108,36 @@
}
/**
- * Returns the refresh rate switching type.
- *
- * @hide
+ * Returns the user preference for "Match content frame rate".
+ * <p>
+ * Never: Even if the app requests it, the device will never try to match its output to the
+ * original frame rate of the content.
+ * </p><p>
+ * Seamless: If the app requests it, the device will match its output to the original frame
+ * rate of the content, ONLY if the display can transition seamlessly.
+ * </p><p>
+ * Always: If the app requests it, the device will match its output to the original
+ * frame rate of the content. This may cause the screen to go blank for a
+ * second when exiting or entering a video playback.
+ * </p>
*/
- @TestApi
- @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE)
- @SwitchingType public int getRefreshRateSwitchingType() {
- return mGlobal.getRefreshRateSwitchingType();
+ @MatchContentFrameRateType public int getMatchContentFrameRateUserPreference() {
+ return toMatchContentFrameRateSetting(mGlobal.getRefreshRateSwitchingType());
+ }
+
+ @MatchContentFrameRateType
+ private int toMatchContentFrameRateSetting(@SwitchingType int switchingType) {
+ switch (switchingType) {
+ case SWITCHING_TYPE_NONE:
+ return MATCH_CONTENT_FRAMERATE_NEVER;
+ case SWITCHING_TYPE_WITHIN_GROUPS:
+ return MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY;
+ case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
+ return MATCH_CONTENT_FRAMERATE_ALWAYS;
+ default:
+ Slog.e(TAG, switchingType + " is not a valid value of switching type.");
+ return MATCH_CONTENT_FRAMERATE_UNKNOWN;
+ }
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index fd0431c5..cdc219a 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -16,7 +16,9 @@
package android.hardware.display;
+
import static android.hardware.display.DisplayManager.EventsMask;
+import static android.view.Display.HdrCapabilities.HdrType;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -508,6 +510,59 @@
}
}
+ /**
+ * Sets the HDR types that have been disabled by user.
+ * @param userDisabledHdrTypes the HDR types to disable. The HDR types are any of
+ */
+ public void setUserDisabledHdrTypes(@HdrType int[] userDisabledHdrTypes) {
+ try {
+ mDm.setUserDisabledHdrTypes(userDisabledHdrTypes);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets whether or not the user disabled HDR types are returned from
+ * {@link Display#getHdrCapabilities}.
+ *
+ * @param areUserDisabledHdrTypesAllowed If true, the user-disabled
+ * types are ignored and returned, if the display supports them. If
+ * false, the user-disabled types are taken into consideration and
+ * are never returned, even if the display supports them.
+ */
+ public void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed) {
+ try {
+ mDm.setAreUserDisabledHdrTypesAllowed(areUserDisabledHdrTypesAllowed);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether or not the user-disabled HDR types are returned from
+ * {@link Display#getHdrCapabilities}.
+ */
+ public boolean areUserDisabledHdrTypesAllowed() {
+ try {
+ return mDm.areUserDisabledHdrTypesAllowed();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the HDR formats disabled by the user.
+ *
+ */
+ public int[] getUserDisabledHdrTypes() {
+ try {
+ return mDm.getUserDisabledHdrTypes();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
public void requestColorMode(int displayId, int colorMode) {
try {
mDm.requestColorMode(displayId, colorMode);
@@ -690,6 +745,37 @@
}
}
+
+ /**
+ * Sets the brightness of the display.
+ *
+ * @param brightness The brightness value from 0.0f to 1.0f.
+ *
+ * @hide
+ */
+ public void setBrightness(int displayId, float brightness) {
+ try {
+ mDm.setBrightness(displayId, brightness);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the brightness of the display.
+ *
+ * @param displayId The display from which to get the brightness
+ *
+ * @hide
+ */
+ public float getBrightness(int displayId) {
+ try {
+ return mDm.getBrightness(displayId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
/**
* Temporarily sets the auto brightness adjustment factor.
* <p>
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index dee9144..5ca4e0c 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -68,6 +68,18 @@
// No permissions required.
WifiDisplayStatus getWifiDisplayStatus();
+ // Requires WRITE_SECURE_SETTINGS permission.
+ void setUserDisabledHdrTypes(in int[] userDisabledTypes);
+
+ // Requires WRITE_SECURE_SETTINGS permission.
+ void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed);
+
+ // No permissions required.
+ boolean areUserDisabledHdrTypesAllowed();
+
+ // No permissions required.
+ int[] getUserDisabledHdrTypes();
+
// Requires CONFIGURE_DISPLAY_COLOR_MODE
void requestColorMode(int displayId, int colorMode);
@@ -119,6 +131,12 @@
// Temporarily sets the display brightness.
void setTemporaryBrightness(int displayId, float brightness);
+ // Saves the display brightness.
+ void setBrightness(int displayId, float brightness);
+
+ // Retrieves the display brightness.
+ float getBrightness(int displayId);
+
// Temporarily sets the auto brightness adjustment factor.
void setTemporaryAutoBrightnessAdjustment(float adjustment);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 01fd396..c83ccfa 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -223,6 +223,14 @@
public static final long BLOCK_FLAG_SLIPPERY = android.os.IInputConstants.BLOCK_FLAG_SLIPPERY;
/**
+ * Check whether apps are using MotionEvent.getRawX/Y. This is implementation-specific, and
+ * thus undefined for most 3p app usages.
+ * @hide
+ */
+ @ChangeId
+ public static final long APP_USES_RAW_INPUT_COORDS = 179274888L;
+
+ /**
* Input Event Injection Synchronization Mode: None.
* Never blocks. Injection is asynchronous and is assumed always to be successful.
* @hide
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 799ea4a..9a8cd79 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -18,7 +18,6 @@
import static android.app.ActivityManager.procStateToString;
import static android.content.pm.PackageManager.GET_SIGNATURES;
-import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -204,9 +203,6 @@
})
public @interface SubscriptionOverrideMask {}
- /** @hide */
- public static final int BLOCKED_METERED_REASON_MASK = 0xffff0000;
-
/**
* Flag to indicate that app is not exempt from any network restrictions.
*
@@ -789,36 +785,6 @@
}
/**
- * Returns whether network access of an UID is blocked or not based on {@code blockedReasons}
- * corresponding to it.
- *
- * {@code blockedReasons} would be a bitwise {@code OR} combination of the
- * {@code BLOCKED_REASON_*} and/or {@code BLOCKED_METERED_REASON_*} constants.
- *
- * @param blockedReasons Value indicating the reasons for why the network access of an UID is
- * blocked. If the value is equal to
- * {@link ConnectivityManager#BLOCKED_REASON_NONE}, then
- * it indicates that an app's network access is not blocked.
- * @param meteredNetwork Value indicating whether the network is metered or not.
- * @return Whether network access is blocked or not.
- * @hide
- */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public static boolean isUidBlocked(int blockedReasons, boolean meteredNetwork) {
- if (blockedReasons == BLOCKED_REASON_NONE) {
- return false;
- }
- final int blockedOnAllNetworksReason = (blockedReasons & ~BLOCKED_METERED_REASON_MASK);
- if (blockedOnAllNetworksReason != BLOCKED_REASON_NONE) {
- return true;
- }
- if (meteredNetwork) {
- return blockedReasons != BLOCKED_REASON_NONE;
- }
- return false;
- }
-
- /**
* Returns the {@code string} representation of {@code blockedReasons} argument.
*
* @param blockedReasons Value indicating the reasons for why the network access of an UID is
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index bc3d5c4..11445e9 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -72,7 +72,7 @@
boolean deviceSupportsNfcSecure();
boolean setNfcSecure(boolean enable);
- boolean setAlwaysOn(boolean value);
- boolean isAlwaysOnEnabled();
- boolean isAlwaysOnSupported();
+ boolean setControllerAlwaysOn(boolean value);
+ boolean isControllerAlwaysOn();
+ boolean isControllerAlwaysOnSupported();
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index e85eb93..eed2c77 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -2254,13 +2254,13 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public boolean setAlwaysOn(boolean value) {
+ @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+ public boolean setControllerAlwaysOn(boolean value) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
try {
- return sService.setAlwaysOn(value);
+ return sService.setControllerAlwaysOn(value);
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
// Try one more time
@@ -2269,7 +2269,7 @@
return false;
}
try {
- return sService.setAlwaysOn(value);
+ return sService.setControllerAlwaysOn(value);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover NFC Service.");
}
@@ -2286,10 +2286,10 @@
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public boolean isAlwaysOnEnabled() {
+ @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+ public boolean isControllerAlwaysOn() {
try {
- return sService.isAlwaysOnEnabled();
+ return sService.isControllerAlwaysOn();
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
// Try one more time
@@ -2298,7 +2298,7 @@
return false;
}
try {
- return sService.isAlwaysOnEnabled();
+ return sService.isControllerAlwaysOn();
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover NFC Service.");
}
@@ -2315,13 +2315,13 @@
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public boolean isAlwaysOnSupported() {
+ @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+ public boolean isControllerAlwaysOnSupported() {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
try {
- return sService.isAlwaysOnSupported();
+ return sService.isControllerAlwaysOnSupported();
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
// Try one more time
@@ -2330,7 +2330,7 @@
return false;
}
try {
- return sService.isAlwaysOnSupported();
+ return sService.isControllerAlwaysOnSupported();
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover NFC Service.");
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 8dcd937..635f581 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -550,6 +550,16 @@
public final native void markVintfStability();
/**
+ * Use a VINTF-stability binder w/o VINTF requirements. Should be called
+ * on a binder before it is sent out of process.
+ *
+ * This must be called before the object is sent to another process.
+ *
+ * @hide
+ */
+ public final native void forceDowngradeToSystemStability();
+
+ /**
* Flush any Binder commands pending in the current thread to the kernel
* driver. This can be
* useful to call before performing an operation that may block for a long
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index ae7d94c..425e797 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -27,9 +27,9 @@
interface IPowerManager
{
void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws,
- String historyTag);
+ String historyTag, int displayId);
void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName,
- int uidtoblame);
+ int uidtoblame, int displayId);
@UnsupportedAppUsage
void releaseWakeLock(IBinder lock, int flags);
void updateWakeLockUids(IBinder lock, in int[] uids);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2c4d130..9d1fd50 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -36,6 +36,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import com.android.internal.util.Preconditions;
@@ -1143,8 +1144,7 @@
* wake lock, and {@link WakeLock#release release()} when you are done.
* </p><p>
* {@samplecode
- * PowerManager pm = (PowerManager)mContext.getSystemService(
- * Context.POWER_SERVICE);
+ * PowerManager pm = mContext.getSystemService(PowerManager.class);
* PowerManager.WakeLock wl = pm.newWakeLock(
* PowerManager.SCREEN_DIM_WAKE_LOCK
* | PowerManager.ON_AFTER_RELEASE,
@@ -1162,6 +1162,8 @@
* {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead.
* This window flag will be correctly managed by the platform
* as the user moves between applications and doesn't require a special permission.
+ * Additionally using the flag will keep only the appropriate screen on in a
+ * multi-display scenario while using a wake lock will keep every screen powered on.
* </p>
*
* <p>
@@ -1179,7 +1181,7 @@
* can be transformed by java optimizer and obfuscator tools.
* <li>avoid wrapping the tag or a prefix to avoid collision with wake lock
* tags from the platform (e.g. *alarm*).
- * <li>never include personnally identifiable information for privacy
+ * <li>never include personally identifiable information for privacy
* reasons.
* </ul>
* </p>
@@ -1200,7 +1202,27 @@
*/
public WakeLock newWakeLock(int levelAndFlags, String tag) {
validateWakeLockParameters(levelAndFlags, tag);
- return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName());
+ return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName(),
+ Display.INVALID_DISPLAY);
+ }
+
+ /**
+ * Creates a new wake lock with the specified level and flags.
+ * <p>
+ * The wakelock will only apply to the {@link com.android.server.display.DisplayGroup} of the
+ * provided {@code displayId}. If {@code displayId} is {@link Display#INVALID_DISPLAY} then it
+ * will apply to all {@link com.android.server.display.DisplayGroup DisplayGroups}.
+ *
+ * @param levelAndFlags Combination of wake lock level and flag values defining
+ * the requested behavior of the WakeLock.
+ * @param tag Your class name (or other tag) for debugging purposes.
+ * @param displayId The display id to which this wake lock is tied.
+ *
+ * @hide
+ */
+ public WakeLock newWakeLock(int levelAndFlags, String tag, int displayId) {
+ validateWakeLockParameters(levelAndFlags, tag);
+ return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName(), displayId);
}
/** @hide */
@@ -2603,19 +2625,17 @@
private WorkSource mWorkSource;
private String mHistoryTag;
private final String mTraceName;
+ private final int mDisplayId;
- private final Runnable mReleaser = new Runnable() {
- public void run() {
- release(RELEASE_FLAG_TIMEOUT);
- }
- };
+ private final Runnable mReleaser = () -> release(RELEASE_FLAG_TIMEOUT);
- WakeLock(int flags, String tag, String packageName) {
+ WakeLock(int flags, String tag, String packageName, int displayId) {
mFlags = flags;
mTag = tag;
mPackageName = packageName;
mToken = new Binder();
mTraceName = "WakeLock (" + mTag + ")";
+ mDisplayId = displayId;
}
@Override
@@ -2696,7 +2716,7 @@
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
try {
mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
- mHistoryTag);
+ mHistoryTag, mDisplayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index bf859d4..f9e4f73 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -131,6 +131,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @TestApi
public static final int NFC_UID = 1027;
/**
diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java
index 06cff90..5f35332 100644
--- a/core/java/android/os/SystemBatteryConsumer.java
+++ b/core/java/android/os/SystemBatteryConsumer.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.util.Slog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -31,6 +32,7 @@
* {@hide}
*/
public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable {
+ private static final String TAG = "SystemBatteryConsumer";
// ****************
// This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto)
@@ -72,6 +74,8 @@
@DrainType
private final int mDrainType;
+ private final double mPowerConsumedByAppsMah;
+
@DrainType
public int getDrainType() {
return mDrainType;
@@ -80,11 +84,23 @@
private SystemBatteryConsumer(@NonNull SystemBatteryConsumer.Builder builder) {
super(builder.mPowerComponentsBuilder.build());
mDrainType = builder.mDrainType;
+ mPowerConsumedByAppsMah = builder.mPowerConsumedByAppsMah;
+ if (mPowerConsumedByAppsMah > getConsumedPower()) {
+ Slog.wtf(TAG,
+ "Power attributed to apps exceeds total: drain type = " + mDrainType
+ + " total consumed power = " + getConsumedPower()
+ + " power consumed by apps = " + mPowerConsumedByAppsMah);
+ }
}
private SystemBatteryConsumer(Parcel in) {
super(new PowerComponents(in));
mDrainType = in.readInt();
+ mPowerConsumedByAppsMah = in.readDouble();
+ }
+
+ public double getPowerConsumedByApps() {
+ return mPowerConsumedByAppsMah;
}
/**
@@ -94,6 +110,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mDrainType);
+ dest.writeDouble(mPowerConsumedByAppsMah);
}
public static final Creator<SystemBatteryConsumer> CREATOR =
@@ -120,6 +137,7 @@
public static final class Builder extends BaseBuilder<Builder> {
@DrainType
private final int mDrainType;
+ private double mPowerConsumedByAppsMah;
private List<UidBatteryConsumer.Builder> mUidBatteryConsumers;
Builder(int customPowerComponentCount, int customTimeComponentCount,
@@ -129,6 +147,15 @@
}
/**
+ * Sets the amount of power used by this system component that is attributed to apps.
+ * It should not exceed the total consumed power.
+ */
+ public Builder setPowerConsumedByApps(double powerConsumedByAppsMah) {
+ mPowerConsumedByAppsMah = powerConsumedByAppsMah;
+ return this;
+ }
+
+ /**
* Add a UidBatteryConsumer to this SystemBatteryConsumer. For example,
* the UidBatteryConsumer with the UID == {@link Process#BLUETOOTH_UID} should
* be added to the SystemBatteryConsumer with the drain type == {@link
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 64e51e7..671daa0 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -19,6 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
import android.util.Log;
import android.util.MathUtils;
@@ -45,6 +46,8 @@
@Nullable
private final SparseBooleanArray mSupportedEffects;
@Nullable
+ private final SparseBooleanArray mSupportedBraking;
+ @Nullable
private final SparseBooleanArray mSupportedPrimitives;
private final float mQFactor;
private final FrequencyMapping mFrequencyMapping;
@@ -53,17 +56,19 @@
mId = in.readInt();
mCapabilities = in.readLong();
mSupportedEffects = in.readSparseBooleanArray();
+ mSupportedBraking = in.readSparseBooleanArray();
mSupportedPrimitives = in.readSparseBooleanArray();
mQFactor = in.readFloat();
mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
}
/** @hide */
- public VibratorInfo(int id, long capabilities, int[] supportedEffects,
+ public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedBraking,
int[] supportedPrimitives, float qFactor, @NonNull FrequencyMapping frequencyMapping) {
mId = id;
mCapabilities = capabilities;
mSupportedEffects = toSparseBooleanArray(supportedEffects);
+ mSupportedBraking = toSparseBooleanArray(supportedBraking);
mSupportedPrimitives = toSparseBooleanArray(supportedPrimitives);
mQFactor = qFactor;
mFrequencyMapping = frequencyMapping;
@@ -74,6 +79,7 @@
dest.writeInt(mId);
dest.writeLong(mCapabilities);
dest.writeSparseBooleanArray(mSupportedEffects);
+ dest.writeSparseBooleanArray(mSupportedBraking);
dest.writeSparseBooleanArray(mSupportedPrimitives);
dest.writeFloat(mQFactor);
dest.writeParcelable(mFrequencyMapping, flags);
@@ -95,6 +101,7 @@
VibratorInfo that = (VibratorInfo) o;
return mId == that.mId && mCapabilities == that.mCapabilities
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
+ && Objects.equals(mSupportedBraking, that.mSupportedBraking)
&& Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives)
&& Objects.equals(mQFactor, that.mQFactor)
&& Objects.equals(mFrequencyMapping, that.mFrequencyMapping);
@@ -102,8 +109,8 @@
@Override
public int hashCode() {
- return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives,
- mQFactor, mFrequencyMapping);
+ return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
+ mSupportedPrimitives, mQFactor, mFrequencyMapping);
}
@Override
@@ -113,6 +120,7 @@
+ ", mCapabilities=" + Arrays.toString(getCapabilitiesNames())
+ ", mCapabilities flags=" + Long.toBinaryString(mCapabilities)
+ ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
+ + ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames())
+ ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
+ ", mQFactor=" + mQFactor
+ ", mFrequencyMapping=" + mFrequencyMapping
@@ -134,6 +142,23 @@
}
/**
+ * Returns a default value to be applied to composed PWLE effects for braking.
+ *
+ * @return a supported braking value, one of android.hardware.vibrator.Braking.*
+ */
+ public int getDefaultBraking() {
+ if (mSupportedBraking != null) {
+ int size = mSupportedBraking.size();
+ for (int i = 0; i < size; i++) {
+ if (mSupportedBraking.keyAt(i) != Braking.NONE) {
+ return mSupportedBraking.keyAt(i);
+ }
+ }
+ }
+ return Braking.NONE;
+ }
+
+ /**
* Query whether the vibrator supports the given effect.
*
* @param effectId Which effects to query for.
@@ -147,7 +172,7 @@
if (mSupportedEffects == null) {
return Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN;
}
- return mSupportedEffects.get(effectId, false) ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES
+ return mSupportedEffects.get(effectId) ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES
: Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
}
@@ -160,7 +185,7 @@
public boolean isPrimitiveSupported(
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
- && mSupportedPrimitives.get(primitiveId, false);
+ && mSupportedPrimitives.get(primitiveId);
}
/**
@@ -251,12 +276,18 @@
if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
names.add("COMPOSE_EFFECTS");
}
+ if (hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
+ names.add("COMPOSE_PWLE_EFFECTS");
+ }
if (hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
names.add("ALWAYS_ON_CONTROL");
}
if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
names.add("AMPLITUDE_CONTROL");
}
+ if (hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)) {
+ names.add("FREQUENCY_CONTROL");
+ }
if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
names.add("EXTERNAL_CONTROL");
}
@@ -277,6 +308,26 @@
return names;
}
+ private String[] getSupportedBrakingNames() {
+ if (mSupportedBraking == null) {
+ return new String[0];
+ }
+ String[] names = new String[mSupportedBraking.size()];
+ for (int i = 0; i < mSupportedBraking.size(); i++) {
+ switch (mSupportedBraking.keyAt(i)) {
+ case Braking.NONE:
+ names[i] = "NONE";
+ break;
+ case Braking.CLAB:
+ names[i] = "CLAB";
+ break;
+ default:
+ names[i] = Integer.toString(mSupportedBraking.keyAt(i));
+ }
+ }
+ return names;
+ }
+
private String[] getSupportedPrimitivesNames() {
if (mSupportedPrimitives == null) {
return new String[0];
@@ -478,7 +529,8 @@
@Override
public String toString() {
return "FrequencyMapping{"
- + "mMinFrequency=" + mMinFrequencyHz
+ + "mRelativeFrequencyRange=" + mRelativeFrequencyRange
+ + ", mMinFrequency=" + mMinFrequencyHz
+ ", mResonantFrequency=" + mResonantFrequencyHz
+ ", mMaxFrequency="
+ (mMinFrequencyHz + mFrequencyResolutionHz * (mMaxAmplitudes.length - 1))
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8107168..77c794c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2796,12 +2796,20 @@
}
/**
+ * Reason to provide if app IO is blocked/resumed for unknown reasons
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0;
+
+ /**
* Reason to provide if app IO is blocked/resumed because of transcoding
*
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 0;
+ public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1;
/**
* Constants for use with
@@ -2812,7 +2820,8 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "APP_IO_BLOCKED_REASON_" }, value = {
- APP_IO_BLOCKED_REASON_TRANSCODING
+ APP_IO_BLOCKED_REASON_TRANSCODING,
+ APP_IO_BLOCKED_REASON_UNKNOWN,
})
public @interface AppIoBlockedReason {}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 8c105be..ef075e1 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -16,6 +16,7 @@
package android.permission;
+import android.content.AttributionSource;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -85,4 +86,8 @@
boolean setAutoRevokeExempted(String packageName, boolean exempted, int userId);
boolean isAutoRevokeExempted(String packageName, int userId);
+
+ AttributionSource registerAttributionSource(in AttributionSource source);
+
+ boolean isRegisteredAttributionSource(in AttributionSource source);
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 7669586cf..936cbfc 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -44,6 +44,7 @@
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.location.LocationManager;
import android.media.AudioManager;
+import android.content.AttributionSource;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -80,6 +81,9 @@
private static final String LOG_TAG = PermissionManager.class.getName();
/** @hide */
+ public static final String LOG_TAG_TRACE_GRANTS = "PermissionGrantTrace";
+
+ /** @hide */
public static final String KILL_APP_REASON_PERMISSIONS_REVOKED =
"permissions revoked";
/** @hide */
@@ -103,6 +107,8 @@
* Note: Changing this won't do anything on its own - you should also change the filtering in
* {@link #shouldTraceGrant}.
*
+ * See log output for tag {@link #LOG_TAG_TRACE_GRANTS}
+ *
* @hide
*/
public static final boolean DEBUG_TRACE_GRANTS = false;
@@ -319,8 +325,10 @@
}
/** @hide */
- public static boolean shouldTraceGrant(String packageName, String permissionName, int userId) {
+ public static boolean shouldTraceGrant(
+ @NonNull String packageName, @NonNull String permissionName, int userId) {
// To be modified when debugging
+ // template: if ("".equals(packageName) && "".equals(permissionName)) return true;
return false;
}
@@ -348,7 +356,8 @@
@NonNull String permissionName, @NonNull UserHandle user) {
if (DEBUG_TRACE_GRANTS
&& shouldTraceGrant(packageName, permissionName, user.getIdentifier())) {
- Log.i(LOG_TAG, "App " + mContext.getPackageName() + " is granting " + packageName + " "
+ Log.i(LOG_TAG_TRACE_GRANTS, "App " + mContext.getPackageName() + " is granting "
+ + packageName + " "
+ permissionName + " for user " + user.getIdentifier(), new RuntimeException());
}
try {
@@ -1091,6 +1100,48 @@
callingFeatureId, pid, uid);
}
+ /**
+ * Registers an attribution source with the OS. An app can only register an attribution
+ * source for itself. Once an attribution source has been registered another app can
+ * check whether this registration exists and thus trust the payload in the source
+ * object. This is important for permission checking and specifically for app op blaming
+ * since a malicious app should not be able to force the OS to blame another app
+ * that doesn't participate in an attribution chain.
+ *
+ * @param source The attribution source to register.
+ *
+ * @see #isRegisteredAttributionSource(AttributionSource)
+ *
+ * @hide
+ */
+ public @NonNull AttributionSource registerAttributionSource(@NonNull AttributionSource source) {
+ try {
+ return mPermissionManager.registerAttributionSource(source);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether an attribution source is registered.
+ *
+ * @param source The attribution source to check.
+ * @return Whether this is a registered source.
+ *
+ * @see #registerAttributionSource(AttributionSource)
+ *
+ * @hide
+ */
+ public boolean isRegisteredAttributionSource(@NonNull AttributionSource source) {
+ try {
+ return mPermissionManager.isRegisteredAttributionSource(source);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
/* @hide */
private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) {
final IActivityManager am = ActivityManager.getService();
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 31cf63c..03b5a2e 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -257,6 +257,14 @@
public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
/**
+ * Namespace for all media related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_MEDIA = "media";
+
+ /**
* Namespace for all media native related features.
*
* @hide
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index b704d66..a5a24c0 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -1102,8 +1102,7 @@
// signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for
// MANAGE_DOCUMENTS or associated URI permission here instead
final Uri rootUri = extraUri;
- enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingAttributionTag(),
- null);
+ enforceWritePermissionInner(rootUri, getCallingAttributionSource());
final String rootId = DocumentsContract.getRootId(rootUri);
ejectRoot(rootId);
@@ -1121,8 +1120,7 @@
}
if (METHOD_IS_CHILD_DOCUMENT.equals(method)) {
- enforceReadPermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceReadPermissionInner(documentUri, getCallingAttributionSource());
final Uri childUri = extraTargetUri;
final String childAuthority = childUri.getAuthority();
@@ -1134,8 +1132,7 @@
&& isChildDocument(documentId, childId));
} else if (METHOD_CREATE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -1149,8 +1146,7 @@
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
} else if (METHOD_CREATE_WEB_LINK_INTENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
final Bundle options = extras.getBundle(DocumentsContract.EXTRA_OPTIONS);
final IntentSender intentSender = createWebLinkIntent(documentId, options);
@@ -1158,8 +1154,7 @@
out.putParcelable(DocumentsContract.EXTRA_RESULT, intentSender);
} else if (METHOD_RENAME_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
final String newDocumentId = renameDocument(documentId, displayName);
@@ -1183,8 +1178,7 @@
}
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
deleteDocument(documentId);
// Document no longer exists, clean up any grants.
@@ -1194,10 +1188,8 @@
final Uri targetUri = extraTargetUri;
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceReadPermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
- enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingAttributionTag(),
- null);
+ enforceReadPermissionInner(documentUri, getCallingAttributionSource());
+ enforceWritePermissionInner(targetUri, getCallingAttributionSource());
final String newDocumentId = copyDocument(documentId, targetId);
@@ -1220,12 +1212,9 @@
final Uri targetUri = extraTargetUri;
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
- enforceReadPermissionInner(parentSourceUri, getCallingPackage(),
- getCallingAttributionTag(), null);
- enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingAttributionTag(),
- null);
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
+ enforceReadPermissionInner(parentSourceUri, getCallingAttributionSource());
+ enforceWritePermissionInner(targetUri, getCallingAttributionSource());
final String newDocumentId = moveDocument(documentId, parentSourceId, targetId);
@@ -1246,10 +1235,8 @@
final Uri parentSourceUri = extraParentUri;
final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
- enforceReadPermissionInner(parentSourceUri, getCallingPackage(),
- getCallingAttributionTag(), null);
- enforceWritePermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceReadPermissionInner(parentSourceUri, getCallingAttributionSource());
+ enforceWritePermissionInner(documentUri, getCallingAttributionSource());
removeDocument(documentId, parentSourceId);
// It's responsibility of the provider to revoke any grants, as the document may be
@@ -1258,8 +1245,7 @@
final boolean isTreeUri = isTreeUri(documentUri);
if (isTreeUri) {
- enforceReadPermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
+ enforceReadPermissionInner(documentUri, getCallingAttributionSource());
} else {
getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 719c383..22d74ca 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -470,8 +470,8 @@
* to be shown, with the "package" scheme. That is "package:com.my.app".
* <p>
* Output: Nothing.
- * @hide
*/
+ @SuppressLint("ActionValue")
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS =
"com.android.settings.APP_OPEN_BY_DEFAULT_SETTINGS";
@@ -2754,7 +2754,7 @@
arg.putBoolean(CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY, true);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
- cp.call(cr.getPackageName(), cr.getAttributionTag(),
+ cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
@@ -2774,7 +2774,7 @@
args.putString(CALL_METHOD_PREFIX_KEY, prefix);
args.putSerializable(CALL_METHOD_FLAGS_KEY, keyValues);
IContentProvider cp = mProviderHolder.getProvider(cr);
- Bundle bundle = cp.call(cr.getPackageName(), cr.getAttributionTag(),
+ Bundle bundle = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(),
mCallSetAllCommand, null, args);
return bundle.getBoolean(KEY_CONFIG_SET_RETURN);
@@ -2862,14 +2862,14 @@
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
+ b = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallGetCommand, name,
args);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
+ b = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args);
}
if (b != null) {
@@ -2939,13 +2939,13 @@
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri,
+ c = cp.query(cr.getAttributionSource(), mUri,
SELECT_VALUE_PROJECTION, queryArgs, null);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri,
+ c = cp.query(cr.getAttributionSource(), mUri,
SELECT_VALUE_PROJECTION, queryArgs, null);
}
if (c == null) {
@@ -3051,7 +3051,7 @@
}
// Fetch all flags for the namespace at once for caching purposes
- Bundle b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
+ Bundle b = cp.call(cr.getAttributionSource(),
mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
if (b == null) {
// Invalid response, return an empty map
@@ -5877,7 +5877,7 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
+ cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SECURE, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
@@ -6291,6 +6291,20 @@
"selected_input_method_subtype";
/**
+ * The {@link android.view.inputmethod.InputMethodInfo.InputMethodInfo#getId() ID} of the
+ * default voice input method.
+ * <p>
+ * This stores the last known default voice IME. If the related system config value changes,
+ * this is reset by InputMethodManagerService.
+ * <p>
+ * This IME is not necessarily in the enabled IME list. That state is still stored in
+ * {@link #ENABLED_INPUT_METHODS}.
+ *
+ * @hide
+ */
+ public static final String DEFAULT_VOICE_INPUT_METHOD = "default_voice_input_method";
+
+ /**
* Setting to record the history of input method subtype, holding the pair of ID of IME
* and its last used subtype.
* @hide
@@ -9162,6 +9176,22 @@
public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
/**
+ * Whether the assistant can be triggered by a touch gesture.
+ *
+ * @hide
+ */
+ public static final String ASSIST_TOUCH_GESTURE_ENABLED =
+ "assist_touch_gesture_enabled";
+
+ /**
+ * Whether the assistant can be triggered by long-pressing the home button
+ *
+ * @hide
+ */
+ public static final String ASSIST_LONG_PRESS_HOME_ENABLED =
+ "assist_long_press_home_enabled";
+
+ /**
* Control whether Trust Agents are in active unlock or extend unlock mode.
* @hide
*/
@@ -9726,6 +9756,12 @@
"accessibility_magnification_mode";
/**
+ * Magnification mode value that is a default value for the magnification logging feature.
+ * @hide
+ */
+ public static final int ACCESSIBILITY_MAGNIFICATION_MODE_NONE = 0x0;
+
+ /**
* Magnification mode value that magnifies whole display.
* @hide
*/
@@ -9757,6 +9793,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY =
"accessibility_magnification_capability";
@@ -14041,6 +14078,40 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final int ZEN_MODE_ALARMS = 3;
+ /**
+ * A comma-separated list of HDR formats that have been disabled by the user.
+ * <p>
+ * If present, these formats will not be reported to apps, even if the display supports
+ * them. This list is treated as empty if the ARE_USER_DISABLED_HDR_FORMATS_ALLOWED setting
+ * is '1'.
+ * </p>
+ * @hide
+ */
+ @TestApi
+ @Readable
+ @SuppressLint("NoSettingsProvider")
+ public static final String USER_DISABLED_HDR_FORMATS = "user_disabled_hdr_formats";
+
+ /**
+ * Whether or not user-disabled HDR formats are allowed.
+ * <p>
+ * The value is boolean (1 or 0). The value '1' means the user preference for disabling a
+ * format is ignored, and the disabled formats are still reported to apps (if supported
+ * by the display). The value '0' means the user-disabled formats are not reported to
+ * apps, even if the display supports them.
+ * </p><p>
+ * The list of formats disabled by the user are contained in the
+ * USER_DISABLED_HDR_FORMATS setting. This list is treated as empty when the value of
+ * this setting is '1'.
+ * </p>
+ * @hide
+ */
+ @TestApi
+ @Readable
+ @SuppressLint("NoSettingsProvider")
+ public static final String ARE_USER_DISABLED_HDR_FORMATS_ALLOWED =
+ "are_user_disabled_hdr_formats_allowed";
+
/** @hide */ public static String zenModeToString(int mode) {
if (mode == ZEN_MODE_IMPORTANT_INTERRUPTIONS) return "ZEN_MODE_IMPORTANT_INTERRUPTIONS";
if (mode == ZEN_MODE_ALARMS) return "ZEN_MODE_ALARMS";
@@ -14865,7 +14936,7 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
+ cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_GLOBAL, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
@@ -16004,7 +16075,7 @@
arg.putString(Settings.CALL_METHOD_PREFIX_KEY, createPrefix(namespace));
}
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
+ cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_CONFIG, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset to defaults for " + DeviceConfig.CONTENT_URI, e);
@@ -16033,7 +16104,7 @@
arg.putInt(CALL_METHOD_USER_KEY, userHandle);
arg.putParcelable(CALL_METHOD_MONITOR_CALLBACK_KEY, callback);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), resolver.getAttributionTag(),
+ cp.call(resolver.getAttributionSource(),
sProviderHolder.mUri.getAuthority(),
CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG, null, arg);
} catch (RemoteException e) {
diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING
index ae2836f..c55572e 100644
--- a/core/java/android/provider/TEST_MAPPING
+++ b/core/java/android/provider/TEST_MAPPING
@@ -19,6 +19,14 @@
},
{
"name": "SettingsProviderTest"
+ },
+ {
+ "name": "CtsAppSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.appsecurity.cts.ReadableSettingsFieldsTest"
+ }
+ ]
}
]
}
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index aeeaa97..d9a310f 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -618,7 +618,7 @@
mFirstOnSuccessTime = SystemClock.elapsedRealtime();
duration = mFirstOnSuccessTime - mFirstRequestTime;
if (sDebug) {
- Log.d(TAG, "Service responded nothing in " + formatDuration(duration));
+ Log.d(TAG, "Inline response in " + formatDuration(duration));
}
}
} break;
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index ad6316c..3ad80d3 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.content.pm.DataLoaderParams;
@@ -41,25 +40,15 @@
/**
* The base class for implementing data loader service to control data loaders. Expecting
- * Incremental Service to bind to a children class of this.
- *
- * WARNING: This is a system API to aid internal development.
- * Use at your own risk. It will change or be removed without warning.
- *
- * TODO(b/136132412): update with latest API design
- *
- * @hide
+ * Installation Session to bind to a children class of this.
*/
-@SystemApi
public abstract class DataLoaderService extends Service {
private static final String TAG = "DataLoaderService";
private final DataLoaderBinderService mBinder = new DataLoaderBinderService();
/**
* Managed DataLoader interface. Each instance corresponds to a single installation session.
- * @hide
*/
- @SystemApi
public interface DataLoader {
/**
* A virtual constructor.
@@ -88,9 +77,7 @@
* DataLoader factory method.
*
* @return An instance of a DataLoader.
- * @hide
*/
- @SystemApi
public @Nullable DataLoader onCreateDataLoader(@NonNull DataLoaderParams dataLoaderParams) {
return null;
}
@@ -156,10 +143,7 @@
/**
* Used by the DataLoaderService implementations.
- *
- * @hide
*/
- @SystemApi
public static final class FileSystemConnector {
/**
* Create a wrapper for a native instance.
diff --git a/core/java/android/service/displayhash/DisplayHasherService.java b/core/java/android/service/displayhash/DisplayHasherService.java
index 2105d84..d300cf1 100644
--- a/core/java/android/service/displayhash/DisplayHasherService.java
+++ b/core/java/android/service/displayhash/DisplayHasherService.java
@@ -52,6 +52,16 @@
"android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
/**
+ * Name under which a DisplayHasherService component publishes information
+ * about itself. This meta-data must reference an XML resource containing a
+ * {@link com.android.internal.R.styleable#DisplayHasherService} tag.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SERVICE_META_DATA = "android.displayhash.display_hasher_service";
+
+ /**
* The {@link Intent} action that must be declared as handled by a service in its manifest
* for the system to recognize it as a DisplayHash providing service.
*
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index da1f457..05f37d7 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -175,7 +175,7 @@
*
* @param packageName the package name of the ANR'ing app
* @param uid the uid of the ANR'ing app
- * @param tid the tid of the ANR'ing app
+ * @param tid the thread id of the ANR'ing app
* @param reason the reason the app is ANR'ing
*/
public void onAnrDelayStarted(@NonNull String packageName, int uid, int tid,
diff --git a/core/java/android/service/voice/AbstractHotwordDetector.java b/core/java/android/service/voice/AbstractHotwordDetector.java
new file mode 100644
index 0000000..e4eefc4
--- /dev/null
+++ b/core/java/android/service/voice/AbstractHotwordDetector.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.service.voice;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.AudioFormat;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.app.IVoiceInteractionManagerService;
+
+/** Base implementation of {@link HotwordDetector}. */
+abstract class AbstractHotwordDetector implements HotwordDetector {
+ private static final String TAG = AbstractHotwordDetector.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private final IVoiceInteractionManagerService mManagerService;
+ private final Handler mHandler;
+ private final HotwordDetector.Callback mCallback;
+
+ AbstractHotwordDetector(
+ IVoiceInteractionManagerService managerService,
+ HotwordDetector.Callback callback) {
+ mManagerService = managerService;
+ // TODO: this needs to be supplied from above
+ mHandler = new Handler(Looper.getMainLooper());
+ mCallback = callback;
+ }
+
+ /**
+ * Detect hotword from an externally supplied stream of data.
+ *
+ * @return a writeable file descriptor that clients can start writing data in the given format.
+ * In order to stop detection, clients can close the given stream.
+ */
+ @Nullable
+ @Override
+ public boolean startRecognition(
+ @NonNull ParcelFileDescriptor audioStream,
+ @NonNull AudioFormat audioFormat,
+ @Nullable PersistableBundle options) {
+ if (DEBUG) {
+ Slog.i(TAG, "#recognizeHotword");
+ }
+
+ // TODO: consider closing existing session.
+
+ try {
+ mManagerService.startListeningFromExternalSource(
+ audioStream,
+ audioFormat,
+ options,
+ new BinderCallback(mHandler, mCallback));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+
+ return true;
+ }
+
+ private static class BinderCallback
+ extends IMicrophoneHotwordDetectionVoiceInteractionCallback.Stub {
+ private final Handler mHandler;
+ // TODO: these need to be weak references.
+ private final HotwordDetector.Callback mCallback;
+
+ BinderCallback(Handler handler, HotwordDetector.Callback callback) {
+ this.mHandler = handler;
+ this.mCallback = callback;
+ }
+
+ /** TODO: onDetected */
+ @Override
+ public void onDetected(
+ @Nullable HotwordDetectedResult hotwordDetectedResult,
+ @Nullable AudioFormat audioFormat,
+ @Nullable ParcelFileDescriptor audioStreamIgnored) {
+ mHandler.sendMessage(obtainMessage(
+ HotwordDetector.Callback::onDetected,
+ mCallback,
+ new AlwaysOnHotwordDetector.EventPayload(audioFormat, hotwordDetectedResult)));
+ }
+ }
+}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 1ea40be..8ca0e7c 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -44,9 +44,11 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.service.voice.HotwordDetectionService.InitializationStatus;
import android.util.Slog;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
@@ -67,7 +69,7 @@
* mark and track it as such.
*/
@SystemApi
-public class AlwaysOnHotwordDetector {
+public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
//---- States of Keyphrase availability. Return codes for onAvailabilityChanged() ----//
/**
* Indicates that this hotword detector is no longer valid for any recognition
@@ -259,6 +261,7 @@
private static final int MSG_DETECTION_PAUSE = 4;
private static final int MSG_DETECTION_RESUME = 5;
private static final int MSG_HOTWORD_REJECTED = 6;
+ private static final int MSG_HOTWORD_STATUS_REPORTED = 7;
private final String mText;
private final Locale mLocale;
@@ -342,14 +345,35 @@
// Raw data associated with the event.
// This is the audio that triggered the keyphrase if {@code isTriggerAudio} is true.
private final byte[] mData;
+ private final HotwordDetectedResult mHotwordDetectedResult;
+ private final ParcelFileDescriptor mAudioStream;
private EventPayload(boolean triggerAvailable, boolean captureAvailable,
AudioFormat audioFormat, int captureSession, byte[] data) {
+ this(triggerAvailable, captureAvailable, audioFormat, captureSession, data, null,
+ null);
+ }
+
+ EventPayload(AudioFormat audioFormat, HotwordDetectedResult hotwordDetectedResult) {
+ this(false, false, audioFormat, -1, null, hotwordDetectedResult, null);
+ }
+
+ EventPayload(AudioFormat audioFormat,
+ HotwordDetectedResult hotwordDetectedResult,
+ ParcelFileDescriptor audioStream) {
+ this(false, false, audioFormat, -1, null, hotwordDetectedResult, audioStream);
+ }
+
+ private EventPayload(boolean triggerAvailable, boolean captureAvailable,
+ AudioFormat audioFormat, int captureSession, byte[] data,
+ HotwordDetectedResult hotwordDetectedResult, ParcelFileDescriptor audioStream) {
mTriggerAvailable = triggerAvailable;
mCaptureAvailable = captureAvailable;
mCaptureSession = captureSession;
mAudioFormat = audioFormat;
mData = data;
+ mHotwordDetectedResult = hotwordDetectedResult;
+ mAudioStream = audioStream;
}
/**
@@ -405,12 +429,39 @@
return null;
}
}
+
+ /**
+ * Returns {@link HotwordDetectedResult} associated with the hotword event, passed from
+ * {@link HotwordDetectionService}.
+ */
+ @Nullable
+ public HotwordDetectedResult getHotwordDetectedResult() {
+ return mHotwordDetectedResult;
+ }
+
+ /**
+ * Returns a stream with bytes corresponding to the open audio stream with hotword data.
+ *
+ * <p>This data represents an audio stream in the format returned by
+ * {@link #getCaptureAudioFormat}.
+ *
+ * <p>Clients are expected to start consuming the stream within 1 second of receiving the
+ * event.
+ *
+ * <p>When this method returns a non-null, clients must close this stream when it's no
+ * longer needed. Failing to do so will result in microphone being open for longer periods
+ * of time, and app being attributed for microphone usage.
+ */
+ @Nullable
+ public ParcelFileDescriptor getAudioStream() {
+ return mAudioStream;
+ }
}
/**
* Callbacks for always-on hotword detection.
*/
- public static abstract class Callback {
+ public abstract static class Callback implements HotwordDetector.Callback {
/**
* Updates the availability state of the active keyphrase and locale on every keyphrase
@@ -474,6 +525,15 @@
*/
public void onRejected(@Nullable HotwordRejectedResult result) {
}
+
+ /**
+ * Called when the {@link HotwordDetectionService} is created by the system and given a
+ * short amount of time to report it's initialization state.
+ *
+ * @param status Info about initialization state of {@link HotwordDetectionService}.
+ */
+ public void onHotwordDetectionServiceInitialized(@InitializationStatus int status) {
+ }
}
/**
@@ -498,17 +558,19 @@
IVoiceInteractionManagerService modelManagementService, int targetSdkVersion,
boolean supportHotwordDetectionService, @Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory) {
+ super(modelManagementService, callback);
+
+ mHandler = new MyHandler();
mText = text;
mLocale = locale;
mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
mExternalCallback = callback;
- mHandler = new MyHandler();
mInternalCallback = new SoundTriggerListener(mHandler);
mModelManagementService = modelManagementService;
mTargetSdkVersion = targetSdkVersion;
mSupportHotwordDetectionService = supportHotwordDetectionService;
if (mSupportHotwordDetectionService) {
- setHotwordDetectionServiceConfig(options, sharedMemory);
+ updateStateLocked(options, sharedMemory, mInternalCallback);
}
try {
Identity identity = new Identity();
@@ -524,30 +586,42 @@
/**
* Set configuration and pass read-only data to hotword detection service.
*
- * @param options Application configuration data provided by the
- * {@link VoiceInteractionService}. PersistableBundle does not allow any remotable objects or
+ * @param options Application configuration data to provide to the
+ * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
* other contents that can be used to communicate with other processes.
- * @param sharedMemory The unrestricted data blob provided by the
- * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+ * @param sharedMemory The unrestricted data blob to provide to the
+ * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
* such data to the trusted process.
*
- * @throws IllegalStateException if it doesn't support hotword detection service.
- *
- * @hide
+ * @throws IllegalStateException if this AlwaysOnHotwordDetector wasn't specified to use a
+ * {@link HotwordDetectionService} when it was created. In addition, if this
+ * AlwaysOnHotwordDetector is in an invalid or error state.
*/
- public final void setHotwordDetectionServiceConfig(@Nullable PersistableBundle options,
+ public final void updateState(@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory) {
if (DBG) {
- Slog.d(TAG, "setHotwordDetectionServiceConfig()");
+ Slog.d(TAG, "updateState()");
}
- if (!mSupportHotwordDetectionService) {
- throw new IllegalStateException(
- "setHotwordDetectionServiceConfig called, but it doesn't support hotword"
- + " detection service");
+ synchronized (mLock) {
+ if (!mSupportHotwordDetectionService) {
+ throw new IllegalStateException(
+ "updateState called, but it doesn't support hotword detection service");
+ }
+ if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+ throw new IllegalStateException(
+ "updateState called on an invalid detector or error state");
+ }
+ updateStateLocked(options, sharedMemory, null /* callback */);
}
+ }
+ private void updateStateLocked(@Nullable PersistableBundle options,
+ @Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
+ if (DBG) {
+ Slog.d(TAG, "updateStateLocked()");
+ }
try {
- mModelManagementService.setHotwordDetectionServiceConfig(options, sharedMemory);
+ mModelManagementService.updateState(options, sharedMemory, callback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -658,6 +732,17 @@
}
/**
+ * Starts recognition for the associated keyphrase.
+ *
+ * @see #startRecognition(int)
+ */
+ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
+ @Override
+ public boolean startRecognition() {
+ return startRecognition(0);
+ }
+
+ /**
* Stops recognition for the associated keyphrase.
* Caller must be the active voice interaction service via
* Settings.Secure.VOICE_INTERACTION_SERVICE.
@@ -671,6 +756,7 @@
* {@link VoiceInteractionService} hosting this detector has been shut down.
*/
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
+ @Override
public boolean stopRecognition() {
if (DBG) Slog.d(TAG, "stopRecognition()");
synchronized (mLock) {
@@ -1086,6 +1172,18 @@
Slog.i(TAG, "onRecognitionResumed");
mHandler.sendEmptyMessage(MSG_DETECTION_RESUME);
}
+
+ @Override
+ public void onStatusReported(int status) {
+ if (DBG) {
+ Slog.d(TAG, "onStatusReported(" + status + ")");
+ } else {
+ Slog.i(TAG, "onStatusReported");
+ }
+ Message message = Message.obtain(mHandler, MSG_HOTWORD_STATUS_REPORTED);
+ message.arg1 = status;
+ message.sendToTarget();
+ }
}
class MyHandler extends Handler {
@@ -1117,6 +1215,9 @@
case MSG_HOTWORD_REJECTED:
mExternalCallback.onRejected((HotwordRejectedResult) msg.obj);
break;
+ case MSG_HOTWORD_STATUS_REPORTED:
+ mExternalCallback.onHotwordDetectionServiceInitialized(msg.arg1);
+ break;
default:
super.handleMessage(msg);
}
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 686268c..7c14c2e 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -20,6 +20,7 @@
import android.annotation.CallSuper;
import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -36,7 +37,13 @@
import android.os.SharedMemory;
import android.util.Log;
+import com.android.internal.app.IHotwordRecognitionStatusCallback;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
+import java.util.function.IntConsumer;
/**
* Implemented by an application that wants to offer detection for hotword. The system will
@@ -50,6 +57,57 @@
// TODO (b/177502877): Set the Debug flag to false before shipping.
private static final boolean DBG = true;
+ private static final long UPDATE_TIMEOUT_MILLIS = 5000;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "INITIALIZATION_STATUS_" }, value = {
+ INITIALIZATION_STATUS_SUCCESS,
+ INITIALIZATION_STATUS_CUSTOM_ERROR_1,
+ INITIALIZATION_STATUS_CUSTOM_ERROR_2,
+ INITIALIZATION_STATUS_UNKNOWN,
+ })
+ public @interface InitializationStatus {}
+
+ /**
+ * Indicates that the updated status is successful.
+ */
+ public static final int INITIALIZATION_STATUS_SUCCESS = 0;
+
+ /**
+ * Indicates that the updated status is failure for some application specific reasons.
+ */
+ public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1;
+
+ /**
+ * Indicates that the updated status is failure for some application specific reasons.
+ */
+ public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_2 = 2;
+
+ /**
+ * Indicates that the callback wasn’t invoked within the timeout.
+ * This is used by system.
+ */
+ public static final int INITIALIZATION_STATUS_UNKNOWN = 100;
+
+ /**
+ * Source for the given audio stream.
+ *
+ * @hide
+ */
+ @Documented
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ AUDIO_SOURCE_MICROPHONE,
+ AUDIO_SOURCE_EXTERNAL
+ })
+ @interface AudioSource {}
+
+ /** @hide */
+ public static final int AUDIO_SOURCE_MICROPHONE = 1;
+ /** @hide */
+ public static final int AUDIO_SOURCE_EXTERNAL = 2;
+
/**
* The {@link Intent} that must be declared as handled by the service.
* To be supported, the service must also require the
@@ -73,24 +131,59 @@
if (DBG) {
Log.d(TAG, "#detectFromDspSource");
}
- mHandler.sendMessage(obtainMessage(HotwordDetectionService::onDetectFromDspSource,
+ mHandler.sendMessage(obtainMessage(HotwordDetectionService::onDetect,
HotwordDetectionService.this,
audioStream,
audioFormat,
timeoutMillis,
- new DspHotwordDetectionCallback(callback)));
+ new Callback(callback)));
}
@Override
- public void setConfig(PersistableBundle options, SharedMemory sharedMemory)
- throws RemoteException {
+ public void updateState(PersistableBundle options, SharedMemory sharedMemory,
+ IHotwordRecognitionStatusCallback callback) throws RemoteException {
if (DBG) {
- Log.d(TAG, "#setConfig");
+ Log.d(TAG, "#updateState");
}
- mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateState,
+ mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateStateInternal,
HotwordDetectionService.this,
options,
- sharedMemory));
+ sharedMemory,
+ callback));
+ }
+
+ @Override
+ public void detectFromMicrophoneSource(
+ ParcelFileDescriptor audioStream,
+ @AudioSource int audioSource,
+ AudioFormat audioFormat,
+ PersistableBundle options,
+ IDspHotwordDetectionCallback callback)
+ throws RemoteException {
+ if (DBG) {
+ Log.d(TAG, "#detectFromMicrophoneSource");
+ }
+ switch (audioSource) {
+ case AUDIO_SOURCE_MICROPHONE:
+ mHandler.sendMessage(obtainMessage(
+ HotwordDetectionService::onDetect,
+ HotwordDetectionService.this,
+ audioStream,
+ audioFormat,
+ new Callback(callback)));
+ break;
+ case AUDIO_SOURCE_EXTERNAL:
+ mHandler.sendMessage(obtainMessage(
+ HotwordDetectionService::onDetect,
+ HotwordDetectionService.this,
+ audioStream,
+ audioFormat,
+ options,
+ new Callback(callback)));
+ break;
+ default:
+ Log.i(TAG, "Unsupported audio source " + audioSource);
+ }
}
};
@@ -113,79 +206,163 @@
}
/**
- * Detect the audio data generated from Dsp.
- *
- * <p>Note: the clients are supposed to call {@code close} on the input stream when they are
- * done with the operation in order to free up resources.
+ * Called when the device hardware (such as a DSP) detected the hotword, to request second stage
+ * validation before handing over the audio to the {@link AlwaysOnHotwordDetector}.
+ * <p>
+ * After {@code callback} is invoked or {@code timeoutMillis} has passed, the system closes
+ * {@code audioStream} and invokes the appropriate {@link AlwaysOnHotwordDetector.Callback
+ * callback}.
*
* @param audioStream Stream containing audio bytes returned from DSP
* @param audioFormat Format of the supplied audio
* @param timeoutMillis Timeout in milliseconds for the operation to invoke the callback. If
* the application fails to abide by the timeout, system will close the
* microphone and cancel the operation.
- * @param callback Use {@link HotwordDetectionService#DspHotwordDetectionCallback} to return
- * the detected result.
+ * @param callback The callback to use for responding to the detection request.
*
* @hide
*/
@SystemApi
- public void onDetectFromDspSource(
+ public void onDetect(
@NonNull ParcelFileDescriptor audioStream,
@NonNull AudioFormat audioFormat,
@DurationMillisLong long timeoutMillis,
- @NonNull DspHotwordDetectionCallback callback) {
+ @NonNull Callback callback) {
+ // TODO: Add a helpful error message.
+ throw new UnsupportedOperationException();
}
/**
* Called when the {@link VoiceInteractionService#createAlwaysOnHotwordDetector(String, Locale,
* PersistableBundle, SharedMemory, AlwaysOnHotwordDetector.Callback)} or
- * {@link AlwaysOnHotwordDetector#setHotwordDetectionServiceConfig(PersistableBundle,
- * SharedMemory)} requests an update of the hotword detection parameters.
+ * {@link AlwaysOnHotwordDetector#updateState(PersistableBundle, SharedMemory)} requests an
+ * update of the hotword detection parameters.
*
- * @param options Application configuration data provided by the
- * {@link VoiceInteractionService}. PersistableBundle does not allow any remotable objects or
+ * @param options Application configuration data to provide to the
+ * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
* other contents that can be used to communicate with other processes.
- * @param sharedMemory The unrestricted data blob provided by the
- * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+ * @param sharedMemory The unrestricted data blob to provide to the
+ * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
* such data to the trusted process.
+ * @param callbackTimeoutMillis Timeout in milliseconds for the operation to invoke the
+ * statusCallback.
+ * @param statusCallback Use this to return the updated result. This is non-null only when the
+ * {@link HotwordDetectionService} is being initialized; and it is null if the state is updated
+ * after that.
*
* @hide
*/
@SystemApi
- public void onUpdateState(@Nullable PersistableBundle options,
- @Nullable SharedMemory sharedMemory) {
+ public void onUpdateState(
+ @Nullable PersistableBundle options,
+ @Nullable SharedMemory sharedMemory,
+ @DurationMillisLong long callbackTimeoutMillis,
+ @Nullable @InitializationStatus IntConsumer statusCallback) {
+ // TODO: Handle the unimplemented case by throwing?
}
/**
- * Callback for returning the detected result.
+ * Called when the {@link VoiceInteractionService} requests that this service
+ * {@link HotwordDetector#startRecognition() start} hotword recognition on audio coming directly
+ * from the device microphone.
+ * <p>
+ * On such a request, the system streams mic audio to this service through {@code audioStream}.
+ * Audio is streamed until {@link HotwordDetector#stopRecognition()} is called, at which point
+ * the system closes {code audioStream}.
+ * <p>
+ * On successful detection of a hotword within {@code audioStream}, call
+ * {@link Callback#onDetected(HotwordDetectedResult)}. The system continues to stream audio
+ * through {@code audioStream}; {@code callback} is reusable.
+ *
+ * @param audioStream Stream containing audio bytes returned from a microphone
+ * @param audioFormat Format of the supplied audio
+ * @param callback The callback to use for responding to the detection request.
+ * {@link Callback#onRejected(HotwordRejectedResult) callback.onRejected} cannot be used here.
+ */
+ public void onDetect(
+ @NonNull ParcelFileDescriptor audioStream,
+ @NonNull AudioFormat audioFormat,
+ @NonNull Callback callback) {
+ // TODO: Add a helpful error message.
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Called when the {@link VoiceInteractionService} requests that this service
+ * {@link HotwordDetector#startRecognition(ParcelFileDescriptor, AudioFormat,
+ * PersistableBundle)} run} hotword recognition on audio coming from an external connected
+ * microphone.
+ * <p>
+ * Upon invoking the {@code callback}, the system closes {@code audioStream} and sends the
+ * detection result to the {@link HotwordDetector.Callback hotword detector}.
+ *
+ * @param audioStream Stream containing audio bytes returned from a microphone
+ * @param audioFormat Format of the supplied audio
+ * @param options Options supporting detection, such as configuration specific to the source of
+ * the audio, provided through
+ * {@link HotwordDetector#startRecognition(ParcelFileDescriptor, AudioFormat,
+ * PersistableBundle)}.
+ * @param callback The callback to use for responding to the detection request.
+ */
+ public void onDetect(
+ @NonNull ParcelFileDescriptor audioStream,
+ @NonNull AudioFormat audioFormat,
+ @Nullable PersistableBundle options,
+ @NonNull Callback callback) {
+ // TODO: Add a helpful error message.
+ throw new UnsupportedOperationException();
+ }
+
+ private void onUpdateStateInternal(@Nullable PersistableBundle options,
+ @Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
+ // TODO (b/183684347): Implement timeout case.
+ IntConsumer intConsumer = null;
+ if (callback != null) {
+ intConsumer =
+ value -> {
+ try {
+ callback.onStatusReported(value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ };
+ }
+ onUpdateState(options, sharedMemory, UPDATE_TIMEOUT_MILLIS, intConsumer);
+ }
+
+ /**
+ * Callback for returning the detection result.
*
* @hide
*/
@SystemApi
- public static final class DspHotwordDetectionCallback {
+ public static final class Callback {
// TODO: need to make sure we don't store remote references, but not a high priority.
private final IDspHotwordDetectionCallback mRemoteCallback;
- private DspHotwordDetectionCallback(IDspHotwordDetectionCallback remoteCallback) {
+ private Callback(IDspHotwordDetectionCallback remoteCallback) {
mRemoteCallback = remoteCallback;
}
/**
* Called when the detected result is valid.
*/
- public void onDetected() {
+ public void onDetected(@Nullable HotwordDetectedResult hotwordDetectedResult) {
try {
- mRemoteCallback.onDetected();
+ mRemoteCallback.onDetected(hotwordDetectedResult);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Informs the {@link AlwaysOnHotwordDetector} that the keyphrase was not detected.
+ * Informs the {@link HotwordDetector} that the keyphrase was not detected.
+ * <p>
+ * This cannot not be used when recognition is done through
+ * {@link #onDetect(ParcelFileDescriptor, AudioFormat, Callback)}.
*
* @param result Info about the second stage detection result. This is provided to
- * the {@link AlwaysOnHotwordDetector}.
+ * the {@link HotwordDetector}.
*/
public void onRejected(@Nullable HotwordRejectedResult result) {
try {
diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java
index abf49b7..f4e5dda 100644
--- a/core/java/android/service/voice/HotwordDetector.java
+++ b/core/java/android/service/voice/HotwordDetector.java
@@ -16,8 +16,18 @@
package android.service.voice;
+import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
+import static android.Manifest.permission.RECORD_AUDIO;
+
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.media.AudioFormat;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.service.voice.HotwordDetectionService.InitializationStatus;
/**
* Basic functionality for hotword detectors.
@@ -40,11 +50,108 @@
int CONFIDENCE_LEVEL_HIGH = 3;
/** @hide */
- @IntDef(prefix = { "CONFIDENCE_LEVEL_" }, value = {
+ @IntDef(prefix = {"CONFIDENCE_LEVEL_"}, value = {
CONFIDENCE_LEVEL_NONE,
CONFIDENCE_LEVEL_LOW,
CONFIDENCE_LEVEL_MEDIUM,
CONFIDENCE_LEVEL_HIGH
})
- @interface HotwordConfidenceLevelValue {}
+ @interface HotwordConfidenceLevelValue {
+ }
+
+ /**
+ * Starts hotword recognition.
+ * <p>
+ * On calling this, the system streams audio from the device microphone to this application's
+ * {@link HotwordDetectionService}. Audio is streamed until {@link #stopRecognition()} is
+ * called.
+ * <p>
+ * On detection of a hotword,
+ * {@link AlwaysOnHotwordDetector.Callback#onDetected(AlwaysOnHotwordDetector.EventPayload)}
+ * is called on the callback provided when creating this {@link HotwordDetector}.
+ * <p>
+ * There is a noticeable impact on battery while recognition is active, so make sure to call
+ * {@link #stopRecognition()} when detection isn't needed.
+ * <p>
+ * Calling this again while recognition is active does nothing.
+ *
+ * @return true if the request to start recognition succeeded
+ */
+ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
+ boolean startRecognition();
+
+ /**
+ * Stops hotword recognition.
+ *
+ * @return true if the request to stop recognition succeeded
+ */
+ boolean stopRecognition();
+
+ /**
+ * Starts hotword recognition on audio coming from an external connected microphone.
+ * <p>
+ * {@link #stopRecognition()} must be called before {@code audioStream} is closed.
+ *
+ * @param audioStream stream containing the audio bytes to run detection on
+ * @param audioFormat format of the encoded audio
+ * @param options options supporting detection, such as configuration specific to the
+ * source of the audio. This will be provided to the {@link HotwordDetectionService}.
+ * PersistableBundle does not allow any remotable objects or other contents that can be
+ * used to communicate with other processes.
+ * @return true if the request to start recognition succeeded
+ */
+ boolean startRecognition(
+ @NonNull ParcelFileDescriptor audioStream,
+ @NonNull AudioFormat audioFormat,
+ @Nullable PersistableBundle options);
+
+ /**
+ * The callback to notify of detection events.
+ */
+ interface Callback {
+
+ /**
+ * Called when the keyphrase is spoken.
+ *
+ * @param eventPayload Payload data for the detection event.
+ */
+ // TODO: Consider creating a new EventPayload that the AOHD one subclasses.
+ void onDetected(@NonNull AlwaysOnHotwordDetector.EventPayload eventPayload);
+
+ /**
+ * Called when the detection fails due to an error.
+ */
+ void onError();
+
+ /**
+ * Called when the recognition is paused temporarily for some reason.
+ * This is an informational callback, and the clients shouldn't be doing anything here
+ * except showing an indication on their UI if they have to.
+ */
+ void onRecognitionPaused();
+
+ /**
+ * Called when the recognition is resumed after it was temporarily paused.
+ * This is an informational callback, and the clients shouldn't be doing anything here
+ * except showing an indication on their UI if they have to.
+ */
+ void onRecognitionResumed();
+
+ /**
+ * Called when the {@link HotwordDetectionService second stage detection} did not detect the
+ * keyphrase.
+ *
+ * @param result Info about the second stage detection result, provided by the
+ * {@link HotwordDetectionService}.
+ */
+ void onRejected(@Nullable HotwordRejectedResult result);
+
+ /**
+ * Called when the {@link HotwordDetectionService} is created by the system and given a
+ * short amount of time to report it's initialization state.
+ *
+ * @param status Info about initialization state of {@link HotwordDetectionService}.
+ */
+ void onHotwordDetectionServiceInitialized(@InitializationStatus int status);
+ }
}
diff --git a/core/java/android/service/voice/IDspHotwordDetectionCallback.aidl b/core/java/android/service/voice/IDspHotwordDetectionCallback.aidl
index 6f641e1..c6b10ff 100644
--- a/core/java/android/service/voice/IDspHotwordDetectionCallback.aidl
+++ b/core/java/android/service/voice/IDspHotwordDetectionCallback.aidl
@@ -16,6 +16,7 @@
package android.service.voice;
+import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordRejectedResult;
/**
@@ -23,11 +24,14 @@
*
* @hide
*/
+// TODO: Rename this.
oneway interface IDspHotwordDetectionCallback {
+
/**
* Called when the detected result is valid.
*/
- void onDetected();
+ void onDetected(
+ in HotwordDetectedResult hotwordDetectedResult);
/**
* Sends {@code result} to the HotwordDetector.
diff --git a/core/java/android/service/voice/IHotwordDetectionService.aidl b/core/java/android/service/voice/IHotwordDetectionService.aidl
index 8d01dd1..cac8333 100644
--- a/core/java/android/service/voice/IHotwordDetectionService.aidl
+++ b/core/java/android/service/voice/IHotwordDetectionService.aidl
@@ -22,6 +22,8 @@
import android.os.SharedMemory;
import android.service.voice.IDspHotwordDetectionCallback;
+import com.android.internal.app.IHotwordRecognitionStatusCallback;
+
/**
* Provide the interface to communicate with hotword detection service.
*
@@ -29,10 +31,18 @@
*/
oneway interface IHotwordDetectionService {
void detectFromDspSource(
- in ParcelFileDescriptor audioStream,
- in AudioFormat audioFormat,
- long timeoutMillis,
- in IDspHotwordDetectionCallback callback);
+ in ParcelFileDescriptor audioStream,
+ in AudioFormat audioFormat,
+ long timeoutMillis,
+ in IDspHotwordDetectionCallback callback);
- void setConfig(in PersistableBundle options, in SharedMemory sharedMemory);
+ void detectFromMicrophoneSource(
+ in ParcelFileDescriptor audioStream,
+ int audioSource,
+ in AudioFormat audioFormat,
+ in PersistableBundle options,
+ in IDspHotwordDetectionCallback callback);
+
+ void updateState(in PersistableBundle options, in SharedMemory sharedMemory,
+ in IHotwordRecognitionStatusCallback callback);
}
diff --git a/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl b/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl
new file mode 100644
index 0000000..80f20fe
--- /dev/null
+++ b/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.service.voice;
+
+import android.media.AudioFormat;
+import android.service.voice.HotwordDetectedResult;
+
+/**
+ * Callback for returning the detected result from the HotwordDetectionService.
+ *
+ * @hide
+ */
+oneway interface IMicrophoneHotwordDetectionVoiceInteractionCallback {
+
+ /**
+ * Called when the detected result is valid.
+ */
+ void onDetected(
+ in HotwordDetectedResult hotwordDetectedResult,
+ in AudioFormat audioFormat,
+ in ParcelFileDescriptor audioStream);
+}
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
new file mode 100644
index 0000000..376596b
--- /dev/null
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.service.voice;
+
+import static android.Manifest.permission.RECORD_AUDIO;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.media.AudioFormat;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.SharedMemory;
+import android.util.Slog;
+
+import com.android.internal.app.IVoiceInteractionManagerService;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages hotword detection not relying on a specific hardware.
+ *
+ * <p>On devices where DSP is available it's strongly recommended to use
+ * {@link AlwaysOnHotwordDetector}.
+ *
+ * @hide
+ **/
+class SoftwareHotwordDetector extends AbstractHotwordDetector {
+ private static final String TAG = SoftwareHotwordDetector.class.getSimpleName();
+ private static final boolean DEBUG = true;
+
+ private final IVoiceInteractionManagerService mManagerService;
+ private final HotwordDetector.Callback mCallback;
+ private final AudioFormat mAudioFormat;
+ private final Handler mHandler;
+ private final Object mLock = new Object();
+
+ SoftwareHotwordDetector(
+ IVoiceInteractionManagerService managerService,
+ AudioFormat audioFormat,
+ PersistableBundle options,
+ SharedMemory sharedMemory,
+ HotwordDetector.Callback callback) {
+ super(managerService, callback);
+
+ mManagerService = managerService;
+ mAudioFormat = audioFormat;
+ mCallback = callback;
+ mHandler = new Handler(Looper.getMainLooper());
+
+ try {
+ mManagerService.updateState(options, sharedMemory, null /* callback */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @RequiresPermission(RECORD_AUDIO)
+ @Override
+ public boolean startRecognition() {
+ if (DEBUG) {
+ Slog.i(TAG, "#startRecognition");
+ }
+
+ maybeCloseExistingSession();
+
+ try {
+ mManagerService.startListeningFromMic(
+ mAudioFormat, new BinderCallback(mHandler, mCallback));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+
+ return true;
+ }
+
+ /** TODO: stopRecognition */
+ @RequiresPermission(RECORD_AUDIO)
+ @Override
+ public boolean stopRecognition() {
+ if (DEBUG) {
+ Slog.i(TAG, "#stopRecognition");
+ }
+
+ try {
+ mManagerService.stopListeningFromMic();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+
+ return true;
+ }
+
+ private void maybeCloseExistingSession() {
+ // TODO: needs to be synchronized.
+ // TODO: implement this
+ }
+
+ private static class BinderCallback
+ extends IMicrophoneHotwordDetectionVoiceInteractionCallback.Stub {
+ private final Handler mHandler;
+ // TODO: this needs to be a weak reference.
+ private final HotwordDetector.Callback mCallback;
+
+ BinderCallback(Handler handler, HotwordDetector.Callback callback) {
+ this.mHandler = handler;
+ this.mCallback = callback;
+ }
+
+ /** TODO: onDetected */
+ @Override
+ public void onDetected(
+ @Nullable HotwordDetectedResult hotwordDetectedResult,
+ @Nullable AudioFormat audioFormat,
+ @Nullable ParcelFileDescriptor audioStream) {
+ mHandler.sendMessage(obtainMessage(
+ HotwordDetector.Callback::onDetected,
+ mCallback,
+ new AlwaysOnHotwordDetector.EventPayload(
+ audioFormat, hotwordDetectedResult, audioStream)));
+ }
+ }
+
+ /** @hide */
+ public void dump(String prefix, PrintWriter pw) {
+ // TODO: implement this
+ }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index cb3791d..2a25227 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
+import android.media.AudioFormat;
import android.media.voice.KeyphraseModelManager;
import android.os.Bundle;
import android.os.Handler;
@@ -134,6 +135,7 @@
private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
private AlwaysOnHotwordDetector mHotwordDetector;
+ private SoftwareHotwordDetector mSoftwareHotwordDetector;
/**
* Called when a user has activated an affordance to launch voice assist from the Keyguard.
@@ -389,6 +391,60 @@
}
/**
+ * Creates a {@link HotwordDetector} and initializes the application's
+ * {@link HotwordDetectionService} using {@code options} and {code sharedMemory}.
+ *
+ * <p>To be able to call this, you need to set android:hotwordDetectionService in the
+ * android.voice_interaction metadata file to a valid hotword detection service, and set
+ * android:isolatedProcess="true" in the hotword detection service's declaration. Otherwise,
+ * this throws an {@link IllegalStateException}.
+ *
+ * <p>This instance must be retained and used by the client.
+ * Calling this a second time invalidates the previously created hotword detector
+ * which can no longer be used to manage recognition.
+ *
+ * <p>Using this has a noticeable impact on battery, since the microphone is kept open
+ * for the lifetime of the recognition {@link HotwordDetector#startRecognition() session}. On
+ * devices where hardware filtering is available (such as through a DSP), it's highly
+ * recommended to use {@link #createAlwaysOnHotwordDetector} instead.
+ *
+ * @param audioFormat Format of the audio to be passed to {@link HotwordDetectionService}.
+ * @param options Application configuration data to be provided to the
+ * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
+ * other contents that can be used to communicate with other processes.
+ * @param sharedMemory The unrestricted data blob to be provided to the
+ * {@link HotwordDetectionService}. Use this to provide hotword models or other such data to the
+ * sandboxed process.
+ * @param callback The callback to notify of detection events.
+ * @return A hotword detector for the given audio format.
+ *
+ * @see #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
+ * AlwaysOnHotwordDetector.Callback)
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION)
+ @NonNull
+ public final HotwordDetector createHotwordDetector(
+ @NonNull AudioFormat audioFormat,
+ @Nullable PersistableBundle options,
+ @Nullable SharedMemory sharedMemory,
+ @NonNull HotwordDetector.Callback callback) {
+ if (mSystemService == null) {
+ throw new IllegalStateException("Not available until onReady() is called");
+ }
+ synchronized (mLock) {
+ // Allow only one concurrent recognition via the APIs.
+ safelyShutdownHotwordDetector();
+ mSoftwareHotwordDetector =
+ new SoftwareHotwordDetector(
+ mSystemService, audioFormat, options, sharedMemory, callback);
+ }
+ return mSoftwareHotwordDetector;
+ }
+
+ /**
* Creates an {@link KeyphraseModelManager} to use for enrolling voice models outside of the
* pre-bundled system voice models.
* @hide
@@ -431,26 +487,45 @@
private void safelyShutdownHotwordDetector() {
synchronized (mLock) {
- if (mHotwordDetector == null) {
- return;
- }
-
- try {
- mHotwordDetector.stopRecognition();
- } catch (Exception ex) {
- // Ignore.
- }
-
- try {
- mHotwordDetector.invalidate();
- } catch (Exception ex) {
- // Ignore.
- }
-
- mHotwordDetector = null;
+ shutdownDspHotwordDetectorLocked();
+ shutdownMicrophoneHotwordDetectorLocked();
}
}
+ private void shutdownDspHotwordDetectorLocked() {
+ if (mHotwordDetector == null) {
+ return;
+ }
+
+ try {
+ mHotwordDetector.stopRecognition();
+ } catch (Exception ex) {
+ // Ignore.
+ }
+
+ try {
+ mHotwordDetector.invalidate();
+ } catch (Exception ex) {
+ // Ignore.
+ }
+
+ mHotwordDetector = null;
+ }
+
+ private void shutdownMicrophoneHotwordDetectorLocked() {
+ if (mSoftwareHotwordDetector == null) {
+ return;
+ }
+
+ try {
+ mSoftwareHotwordDetector.stopRecognition();
+ } catch (Exception ex) {
+ // Ignore.
+ }
+
+ mSoftwareHotwordDetector = null;
+ }
+
/**
* Provide hints to be reflected in the system UI.
*
@@ -478,6 +553,13 @@
} else {
mHotwordDetector.dump(" ", pw);
}
+
+ pw.println(" MicrophoneHotwordDetector");
+ if (mSoftwareHotwordDetector == null) {
+ pw.println(" NULL");
+ } else {
+ mSoftwareHotwordDetector.dump(" ", pw);
+ }
}
}
}
diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl
index cc1cded..9a5e534 100644
--- a/core/java/android/speech/IRecognitionService.aidl
+++ b/core/java/android/speech/IRecognitionService.aidl
@@ -17,6 +17,7 @@
package android.speech;
import android.os.Bundle;
+import android.content.AttributionSource;
import android.content.Intent;
import android.speech.IRecognitionListener;
@@ -39,11 +40,10 @@
* this intent can contain extra parameters to manipulate the behavior of the recognition
* client. For more information see {@link RecognizerIntent}.
* @param listener to receive callbacks, note that this must be non-null
- * @param packageName the package name calling this API
- * @param featureId The feature in the package
+ * @param attributionSource The attribution source of the caller.
*/
void startListening(in Intent recognizerIntent, in IRecognitionListener listener,
- String packageName, String featureId, int callingUid);
+ in AttributionSource attributionSource);
/**
* Stops listening for speech. Speech captured so far will be recognized as
@@ -51,18 +51,14 @@
* is called during the speech capturing.
*
* @param listener to receive callbacks, note that this must be non-null
- * @param packageName the package name calling this API
- * @param featureId The feature in the package
*/
- void stopListening(in IRecognitionListener listener, String packageName, String featureId);
+ void stopListening(in IRecognitionListener listener);
/**
* Cancels the speech recognition.
*
* @param listener to receive callbacks, note that this must be non-null
* @param packageName the package name calling this API
- * @param featureId The feature in the package
- * @param isShutdown Whether the cancellation is caused by a client calling #shutdown
*/
- void cancel(in IRecognitionListener listener, String packageName, String featureId, boolean isShutdown);
+ void cancel(in IRecognitionListener listener, boolean isShutdown);
}
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index fd584f1..4afa9473 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -16,11 +16,16 @@
package android.speech;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.app.AppOpsManager;
import android.app.Service;
+import android.content.Context;
+import android.content.ContextParams;
import android.content.Intent;
import android.content.PermissionChecker;
import android.os.Binder;
@@ -28,13 +33,13 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.content.AttributionSource;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
-import com.android.internal.util.Preconditions;
-
import java.lang.ref.WeakReference;
+import java.util.Objects;
/**
* This class provides a base class for recognition service implementations. This class should be
@@ -86,13 +91,13 @@
switch (msg.what) {
case MSG_START_LISTENING:
StartListeningArgs args = (StartListeningArgs) msg.obj;
- dispatchStartListening(args.mIntent, args.mListener, args.mCallingUid);
+ dispatchStartListening(args.mIntent, args.mListener, args.mAttributionSource);
break;
case MSG_STOP_LISTENING:
dispatchStopListening((IRecognitionListener) msg.obj);
break;
case MSG_CANCEL:
- dispatchCancel((IRecognitionListener) msg.obj);
+ dispatchCancel((IRecognitionListener) msg.obj, msg.arg1 == 1);
break;
case MSG_RESET:
dispatchClearCallback();
@@ -102,10 +107,11 @@
};
private void dispatchStartListening(Intent intent, final IRecognitionListener listener,
- int callingUid) {
+ @NonNull AttributionSource attributionSource) {
if (mCurrentCallback == null) {
if (DBG) Log.d(TAG, "created new mCurrentCallback, listener = " + listener.asBinder());
- mCurrentCallback = new Callback(listener, callingUid);
+ mCurrentCallback = new Callback(listener, attributionSource);
+
RecognitionService.this.onStartListening(intent, mCurrentCallback);
} else {
try {
@@ -133,13 +139,16 @@
}
}
- private void dispatchCancel(IRecognitionListener listener) {
+ private void dispatchCancel(IRecognitionListener listener, boolean shutDown) {
if (mCurrentCallback == null) {
if (DBG) Log.d(TAG, "cancel called with no preceding startListening - ignoring");
} else if (mCurrentCallback.mListener.asBinder() != listener.asBinder()) {
Log.w(TAG, "cancel called by client who did not call startListening - ignoring");
} else { // the correct state
RecognitionService.this.onCancel(mCurrentCallback);
+ if (shutDown) {
+ mCurrentCallback.finishRecordAudioOpAttributionToCallerIfNeeded();
+ }
mCurrentCallback = null;
if (DBG) Log.d(TAG, "canceling - setting mCurrentCallback to null");
}
@@ -153,12 +162,13 @@
public final Intent mIntent;
public final IRecognitionListener mListener;
- public final int mCallingUid;
+ public final @NonNull AttributionSource mAttributionSource;
- public StartListeningArgs(Intent intent, IRecognitionListener listener, int callingUid) {
+ public StartListeningArgs(Intent intent, IRecognitionListener listener,
+ @NonNull AttributionSource attributionSource) {
this.mIntent = intent;
this.mListener = listener;
- this.mCallingUid = callingUid;
+ this.mAttributionSource = attributionSource;
}
}
@@ -247,18 +257,19 @@
*/
public class Callback {
private final IRecognitionListener mListener;
- private final int mCallingUid;
+ private final @NonNull AttributionSource mCallingAttributionSource;
+ private @Nullable Context mAttributionContext;
- private Callback(IRecognitionListener listener, int callingUid) {
+ private Callback(IRecognitionListener listener,
+ @NonNull AttributionSource attributionSource) {
mListener = listener;
- mCallingUid = callingUid;
+ mCallingAttributionSource = attributionSource;
}
/**
* The service should call this method when the user has started to speak.
*/
public void beginningOfSpeech() throws RemoteException {
- if (DBG) Log.d(TAG, "beginningOfSpeech");
mListener.onBeginningOfSpeech();
}
@@ -270,6 +281,7 @@
* single channel audio stream. The sample rate is implementation dependent.
*/
public void bufferReceived(byte[] buffer) throws RemoteException {
+ startRecordAudioOpAttributionToCallerIfNeeded();
mListener.onBufferReceived(buffer);
}
@@ -302,6 +314,7 @@
* {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
*/
public void partialResults(Bundle partialResults) throws RemoteException {
+ startRecordAudioOpAttributionToCallerIfNeeded();
mListener.onPartialResults(partialResults);
}
@@ -323,6 +336,7 @@
* {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
*/
public void results(Bundle results) throws RemoteException {
+ startRecordAudioOpAttributionToCallerIfNeeded();
Message.obtain(mHandler, MSG_RESET).sendToTarget();
mListener.onResults(results);
}
@@ -342,7 +356,65 @@
* is being processed. This is obtained from {@link Binder#getCallingUid()}.
*/
public int getCallingUid() {
- return mCallingUid;
+ return mCallingAttributionSource.getUid();
+ }
+
+ /**
+ * Gets the permission identity of the calling app. If you want to attribute
+ * the mic access to the calling app you can create an attribution context
+ * via {@link android.content.Context#createContext(android.content.ContextParams)}
+ * and passing this identity to {@link
+ * android.content.ContextParams.Builder#setNextAttributionSource(AttributionSource)}.
+ *
+ *
+ *
+ *
+ * @return The permission identity of the calling app.
+ *
+ * @see android.content.ContextParams.Builder#setNextAttributionSource(
+ * AttributionSource)
+ */
+ @SuppressLint("CallbackMethodName")
+ public @NonNull AttributionSource getCallingAttributionSource() {
+ return mCallingAttributionSource;
+ }
+
+ private void startRecordAudioOpAttributionToCallerIfNeeded() throws RemoteException {
+ if (!isProxyingRecordAudioToCaller()) {
+ final int result = PermissionChecker.checkPermissionAndStartDataDelivery(
+ RecognitionService.this, Manifest.permission.RECORD_AUDIO,
+ getAttributionContextForCaller().getAttributionSource(),
+ /*message*/ null);
+ if (result == PermissionChecker.PERMISSION_GRANTED) {
+ return;
+ }
+ error(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
+ }
+ }
+
+ private @NonNull Context getAttributionContextForCaller() {
+ if (mAttributionContext == null) {
+ mAttributionContext = createContext(new ContextParams.Builder()
+ .setNextAttributionSource(mCallingAttributionSource)
+ .build());
+ }
+ return mAttributionContext;
+ }
+
+ void finishRecordAudioOpAttributionToCallerIfNeeded() {
+ if (isProxyingRecordAudioToCaller()) {
+ final String op = AppOpsManager.permissionToOp(Manifest.permission.RECORD_AUDIO);
+ PermissionChecker.finishDataDelivery(RecognitionService.this,
+ op, getAttributionContextForCaller().getAttributionSource());
+ }
+ }
+
+ private boolean isProxyingRecordAudioToCaller() {
+ final int op = AppOpsManager.permissionToOpCode(Manifest.permission.RECORD_AUDIO);
+ final AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
+ return appOpsManager.isProxying(op, getAttributionTag(),
+ mCallingAttributionSource.getUid(),
+ mCallingAttributionSource.getPackageName());
}
}
@@ -356,44 +428,35 @@
@Override
public void startListening(Intent recognizerIntent, IRecognitionListener listener,
- String packageName, String featureId, int callingUid) {
- Preconditions.checkNotNull(packageName);
-
+ @NonNull AttributionSource attributionSource) {
+ Objects.requireNonNull(attributionSource);
+ attributionSource.enforceCallingUid();
if (DBG) Log.d(TAG, "startListening called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener, true /*forDataDelivery*/,
- packageName, featureId)) {
+ if (service != null) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_START_LISTENING, service.new StartListeningArgs(
- recognizerIntent, listener, callingUid)));
+ recognizerIntent, listener, attributionSource)));
}
}
@Override
- public void stopListening(IRecognitionListener listener, String packageName,
- String featureId) {
- Preconditions.checkNotNull(packageName);
-
+ public void stopListening(IRecognitionListener listener) {
if (DBG) Log.d(TAG, "stopListening called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener, false /*forDataDelivery*/,
- packageName, featureId)) {
+ if (service != null) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_STOP_LISTENING, listener));
}
}
@Override
- public void cancel(IRecognitionListener listener, String packageName,
- String featureId, boolean isShutdown) {
- Preconditions.checkNotNull(packageName);
-
+ public void cancel(IRecognitionListener listener, boolean isShutdown) {
if (DBG) Log.d(TAG, "cancel called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener, false /*forDataDelivery*/,
- packageName, featureId)) {
+ if (service != null) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
- MSG_CANCEL, listener));
+ MSG_CANCEL, isShutdown ? 1 : 0, 0, listener));
}
}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 9b93a64..7aa5ee5 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -386,8 +386,7 @@
return;
}
try {
- mService.startListening(recognizerIntent, mListener, mContext.getOpPackageName(),
- mContext.getAttributionTag(), android.os.Process.myUid());
+ mService.startListening(recognizerIntent, mListener, mContext.getAttributionSource());
if (DBG) Log.d(TAG, "service start listening command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "startListening() failed", e);
@@ -401,8 +400,7 @@
return;
}
try {
- mService.stopListening(mListener, mContext.getOpPackageName(),
- mContext.getAttributionTag());
+ mService.stopListening(mListener);
if (DBG) Log.d(TAG, "service stop listening command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "stopListening() failed", e);
@@ -416,11 +414,7 @@
return;
}
try {
- mService.cancel(
- mListener,
- mContext.getOpPackageName(),
- mContext.getAttributionTag(),
- false /* isShutdown */);
+ mService.cancel(mListener, /*isShutdown*/ false);
if (DBG) Log.d(TAG, "service cancel command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "cancel() failed", e);
@@ -463,8 +457,7 @@
public void destroy() {
if (mService != null) {
try {
- mService.cancel(mListener, mContext.getOpPackageName(),
- mContext.getAttributionTag(), true /* isShutdown */);
+ mService.cancel(mListener, /*isShutdown*/ true);
} catch (final RemoteException e) {
// Not important
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index d47ae27..49065aa 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -343,11 +343,14 @@
/**
* Listen for display info changed event.
*
- * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
- * READ_PHONE_STATE} or that the calling app has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges}).
+ * For clients compiled on Android 11 SDK, requires permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling app has carrier
+ * privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ * For clients compiled on Android 12 SDK or newer,
+ * {@link android.Manifest.permission#READ_PHONE_STATE} or carrier privileges is not required
+ * anymore.
*
- * @see #onDisplayInfoChanged
+ * @see #onDisplayInfoChanged
* @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead.
*/
@Deprecated
@@ -700,6 +703,10 @@
* calling {@link TelephonyManager#getCallState()} from within this callback may return a
* different state than the callback reports.
*
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
+ * targeting API level 31+.
+ *
* @param state call state
* @param phoneNumber call phone number. If application does not have
* {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} permission or carrier
@@ -709,6 +716,7 @@
* @deprecated Use {@link TelephonyCallback.CallStateListener} instead.
*/
@Deprecated
+ @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
public void onCallStateChanged(@CallState int state, String phoneNumber) {
// default implementation empty
}
@@ -981,8 +989,12 @@
* <p> The {@link TelephonyDisplayInfo} contains status information shown to the user based on
* carrier policy.
*
- * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
- * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ * For clients compiled on Android 11 SDK, requires permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling app has carrier
+ * privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ * For clients compiled on Android 12 SDK or newer,
+ * {@link android.Manifest.permission#READ_PHONE_STATE} or carrier privileges is not required
+ * anymore.
*
* @param telephonyDisplayInfo The display information.
* @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead.
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index d000000..1ab6e0f 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -752,6 +752,7 @@
*
* @param state the current call state
*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public void onCallStateChanged(@Annotation.CallState int state);
}
@@ -1057,7 +1058,6 @@
*
* @param telephonyDisplayInfo The display information.
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
}
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
index 8c771ba..3d60373 100644
--- a/core/java/android/uwb/AngleMeasurement.java
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -48,7 +48,10 @@
* @throws IllegalArgumentException if the radians, errorRadians, or confidenceLevel is out of
* allowed range
*/
- public AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+ public AngleMeasurement(
+ @FloatRange(from = -Math.PI, to = +Math.PI) double radians,
+ @FloatRange(from = 0.0, to = +Math.PI) double errorRadians,
+ @FloatRange(from = 0.0, to = 1.0) double confidenceLevel) {
if (radians < -Math.PI || radians > Math.PI) {
throw new IllegalArgumentException("Invalid radians: " + radians);
}
diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java
index 547bc9d..b55d9b3 100644
--- a/core/java/android/view/ContentInfo.java
+++ b/core/java/android/view/ContentInfo.java
@@ -26,6 +26,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.util.Pair;
+import android.view.inputmethod.InputContentInfo;
import com.android.internal.util.Preconditions;
@@ -141,6 +142,10 @@
private final Uri mLinkUri;
@Nullable
private final Bundle mExtras;
+ @Nullable
+ private final InputContentInfo mInputContentInfo;
+ @Nullable
+ private final DragAndDropPermissions mDragAndDropPermissions;
private ContentInfo(Builder b) {
this.mClip = Objects.requireNonNull(b.mClip);
@@ -149,6 +154,23 @@
this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
this.mLinkUri = b.mLinkUri;
this.mExtras = b.mExtras;
+ this.mInputContentInfo = b.mInputContentInfo;
+ this.mDragAndDropPermissions = b.mDragAndDropPermissions;
+ }
+
+ /**
+ * If the content came from a source that supports proactive release of URI permissions
+ * (e.g. IME), releases permissions; otherwise a no-op.
+ *
+ * @hide
+ */
+ public void releasePermissions() {
+ if (mInputContentInfo != null) {
+ mInputContentInfo.releasePermission();
+ }
+ if (mDragAndDropPermissions != null) {
+ mDragAndDropPermissions.release();
+ }
}
@NonNull
@@ -275,6 +297,10 @@
private Uri mLinkUri;
@Nullable
private Bundle mExtras;
+ @Nullable
+ private InputContentInfo mInputContentInfo;
+ @Nullable
+ private DragAndDropPermissions mDragAndDropPermissions;
/**
* Creates a new builder initialized with the data from the given builder.
@@ -285,6 +311,8 @@
mFlags = other.mFlags;
mLinkUri = other.mLinkUri;
mExtras = other.mExtras;
+ mInputContentInfo = other.mInputContentInfo;
+ mDragAndDropPermissions = other.mDragAndDropPermissions;
}
/**
@@ -355,6 +383,31 @@
}
/**
+ * Set the {@link InputContentInfo} object if the content is coming from the IME. This can
+ * be used for proactive cleanup of permissions.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setInputContentInfo(@Nullable InputContentInfo inputContentInfo) {
+ mInputContentInfo = inputContentInfo;
+ return this;
+ }
+
+ /**
+ * Set the {@link DragAndDropPermissions} object if the content is coming via drag-and-drop.
+ * This can be used for proactive cleanup of permissions.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setDragAndDropPermissions(@Nullable DragAndDropPermissions permissions) {
+ mDragAndDropPermissions = permissions;
+ return this;
+ }
+
+
+ /**
* @return A new {@link ContentInfo} instance with the data from this builder.
*/
@NonNull
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d484f4d4..bf152cb 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -44,6 +44,7 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -1066,7 +1067,47 @@
public HdrCapabilities getHdrCapabilities() {
synchronized (mLock) {
updateDisplayInfoLocked();
- return mDisplayInfo.hdrCapabilities;
+ if (mDisplayInfo.userDisabledHdrTypes.length == 0) {
+ return mDisplayInfo.hdrCapabilities;
+ }
+ ArraySet<Integer> enabledTypesSet = new ArraySet<>();
+ for (int supportedType : mDisplayInfo.hdrCapabilities.getSupportedHdrTypes()) {
+ boolean typeDisabled = false;
+ for (int userDisabledType : mDisplayInfo.userDisabledHdrTypes) {
+ if (supportedType == userDisabledType) {
+ typeDisabled = true;
+ break;
+ }
+ }
+ if (!typeDisabled) {
+ enabledTypesSet.add(supportedType);
+ }
+ }
+ int[] enabledTypes = new int[enabledTypesSet.size()];
+ int index = 0;
+ for (int enabledType : enabledTypesSet) {
+ enabledTypes[index++] = enabledType;
+ }
+ return new HdrCapabilities(enabledTypes,
+ mDisplayInfo.hdrCapabilities.mMaxLuminance,
+ mDisplayInfo.hdrCapabilities.mMaxAverageLuminance,
+ mDisplayInfo.hdrCapabilities.mMinLuminance);
+ }
+ }
+
+ /**
+ * @hide
+ * Returns the display's HDR supported types.
+ *
+ * @see #isHdr()
+ * @see HdrCapabilities#getSupportedHdrTypes()
+ */
+ @TestApi
+ @NonNull
+ public int[] getReportedHdrTypes() {
+ synchronized (mLock) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.hdrCapabilities.getSupportedHdrTypes();
}
}
@@ -1079,7 +1120,7 @@
public boolean isHdr() {
synchronized (mLock) {
updateDisplayInfoLocked();
- return mDisplayInfo.isHdr();
+ return !(getHdrCapabilities().getSupportedHdrTypes().length == 0);
}
}
@@ -1855,6 +1896,14 @@
public static final int HDR_TYPE_HDR10_PLUS = 4;
/** @hide */
+ public static final int[] HDR_TYPES = {
+ HDR_TYPE_DOLBY_VISION,
+ HDR_TYPE_HDR10,
+ HDR_TYPE_HLG,
+ HDR_TYPE_HDR10_PLUS
+ };
+
+ /** @hide */
@IntDef(prefix = { "HDR_TYPE_" }, value = {
HDR_TYPE_DOLBY_VISION,
HDR_TYPE_HDR10,
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 9aaf5c0..36be9f8 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -198,6 +198,9 @@
/** The display's HDR capabilities */
public Display.HdrCapabilities hdrCapabilities;
+ /** The formats disabled by user **/
+ public int[] userDisabledHdrTypes = {};
+
/**
* Indicates whether the display can be switched into a mode with minimal post
* processing.
@@ -363,6 +366,7 @@
&& colorMode == other.colorMode
&& Arrays.equals(supportedColorModes, other.supportedColorModes)
&& Objects.equals(hdrCapabilities, other.hdrCapabilities)
+ && Arrays.equals(userDisabledHdrTypes, other.userDisabledHdrTypes)
&& minimalPostProcessingSupported == other.minimalPostProcessingSupported
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
@@ -412,6 +416,7 @@
supportedColorModes = Arrays.copyOf(
other.supportedColorModes, other.supportedColorModes.length);
hdrCapabilities = other.hdrCapabilities;
+ userDisabledHdrTypes = other.userDisabledHdrTypes;
minimalPostProcessingSupported = other.minimalPostProcessingSupported;
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
@@ -478,6 +483,11 @@
brightnessMaximum = source.readFloat();
brightnessDefault = source.readFloat();
roundedCorners = source.readTypedObject(RoundedCorners.CREATOR);
+ int numUserDisabledFormats = source.readInt();
+ userDisabledHdrTypes = new int[numUserDisabledFormats];
+ for (int i = 0; i < numUserDisabledFormats; i++) {
+ userDisabledHdrTypes[i] = source.readInt();
+ }
}
@Override
@@ -528,6 +538,10 @@
dest.writeFloat(brightnessMaximum);
dest.writeFloat(brightnessDefault);
dest.writeTypedObject(roundedCorners, flags);
+ dest.writeInt(userDisabledHdrTypes.length);
+ for (int i = 0; i < userDisabledHdrTypes.length; i++) {
+ dest.writeInt(userDisabledHdrTypes[i]);
+ }
}
@Override
@@ -729,6 +743,8 @@
sb.append(Arrays.toString(supportedModes));
sb.append(", hdrCapabilities ");
sb.append(hdrCapabilities);
+ sb.append(", userDisabledHdrTypes ");
+ sb.append(Arrays.toString(userDisabledHdrTypes));
sb.append(", minimalPostProcessingSupported ");
sb.append(minimalPostProcessingSupported);
sb.append(", rotation ");
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e1f13f2..d0a3e4b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -777,6 +777,15 @@
VerifiedDisplayHash verifyDisplayHash(in DisplayHash displayHash);
/**
+ * Call to enable or disable the throttling when generating a display hash. This should only be
+ * used for testing. Throttling is enabled by default.
+ *
+ * Must be called from a process that has {@link android.Manifest.permission#READ_FRAME_BUFFER}
+ * permission.
+ */
+ void setDisplayHashThrottlingEnabled(boolean enable);
+
+ /**
* Registers a listener for a {@link android.window.WindowContext} to handle configuration
* changes from the server side.
* <p>
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 98b7dbf..0686104 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -51,8 +51,8 @@
}
@Override
- public void onWindowFocusGained() {
- super.onWindowFocusGained();
+ public void onWindowFocusGained(boolean hasViewFocus) {
+ super.onWindowFocusGained(hasViewFocus);
getImm().registerImeConsumer(this);
}
@@ -64,8 +64,14 @@
}
@Override
- void hide(boolean animationFinished, @AnimationType int animationType) {
+ public void hide() {
super.hide();
+ mIsRequestedVisibleAwaitingControl = false;
+ }
+
+ @Override
+ void hide(boolean animationFinished, @AnimationType int animationType) {
+ hide();
if (animationFinished) {
// remove IME surface as IME has finished hide animation.
@@ -122,6 +128,9 @@
hide();
removeSurface();
}
+ if (control != null) {
+ mIsRequestedVisibleAwaitingControl = false;
+ }
}
@Override
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index a68f528..c001ec9 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1302,8 +1302,8 @@
/**
* Called when current window gains focus.
*/
- public void onWindowFocusGained() {
- getSourceConsumer(ITYPE_IME).onWindowFocusGained();
+ public void onWindowFocusGained(boolean hasViewFocused) {
+ getSourceConsumer(ITYPE_IME).onWindowFocusGained(hasViewFocused);
}
/**
@@ -1366,8 +1366,9 @@
final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null;
// Skip showing animation once that made by system for some reason.
// (e.g. starting window with IME snapshot)
- if (imeControl != null && show) {
- skipAnim = imeControl.getAndClearSkipAnimationOnce();
+ if (imeControl != null) {
+ skipAnim = imeControl.getAndClearSkipAnimationOnce() && show
+ && consumer.hasViewFocusWhenWindowFocusGain();
}
}
applyAnimation(types, show, fromIme, skipAnim);
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index fd1c3b8..bc50dbe 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -81,6 +81,11 @@
private final Supplier<Transaction> mTransactionSupplier;
private @Nullable InsetsSourceControl mSourceControl;
private boolean mHasWindowFocus;
+
+ /**
+ * Whether the view has focus returned by {@link #onWindowFocusGained(boolean)}.
+ */
+ private boolean mHasViewFocusWhenWindowFocusGain;
private Rect mPendingFrame;
private Rect mPendingVisibleFrame;
@@ -223,8 +228,9 @@
/**
* Called when current window gains focus
*/
- public void onWindowFocusGained() {
+ public void onWindowFocusGained(boolean hasViewFocus) {
mHasWindowFocus = true;
+ mHasViewFocusWhenWindowFocusGain = hasViewFocus;
}
/**
@@ -238,6 +244,10 @@
return mHasWindowFocus;
}
+ boolean hasViewFocusWhenWindowFocusGain() {
+ return mHasViewFocusWhenWindowFocusGain;
+ }
+
boolean applyLocalVisibilityOverride() {
final InsetsSource source = mState.peekSource(mType);
final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6801c27..dea32cd 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.hardware.input.InputManager.APP_USES_RAW_INPUT_COORDS;
import static android.view.Display.DEFAULT_DISPLAY;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -23,6 +24,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.compat.Compatibility;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Matrix;
import android.os.Build;
@@ -2672,6 +2674,7 @@
* @see #AXIS_X
*/
public final float getRawX() {
+ Compatibility.reportChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}
@@ -2685,6 +2688,7 @@
* @see #AXIS_Y
*/
public final float getRawY() {
+ Compatibility.reportChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT);
}
@@ -2701,6 +2705,7 @@
* @see #AXIS_X
*/
public float getRawX(int pointerIndex) {
+ Compatibility.reportChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
}
@@ -2717,6 +2722,7 @@
* @see #AXIS_Y
*/
public float getRawY(int pointerIndex) {
+ Compatibility.reportChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_Y, pointerIndex, HISTORY_CURRENT);
}
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 31f6f6a..4a1d685 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -9,6 +9,7 @@
ogunwale@google.com
jjaggi@google.com
roosa@google.com
+jreck@google.com
# Display
per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS
diff --git a/core/java/android/view/RoundedCorner.java b/core/java/android/view/RoundedCorner.java
index 56b4383..d10fc44 100644
--- a/core/java/android/view/RoundedCorner.java
+++ b/core/java/android/view/RoundedCorner.java
@@ -29,16 +29,10 @@
/**
* Represents a rounded corner of the display.
- *
- * <code>
- * ________
- * / ^
- * / | Radius
- * | v
- * | X <- Center point
- * |<----->
- * Radius
- * </code>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/rounded_corner/rounded-corner-info.png" height="120"
+ * alt="A figure to describe what the rounded corner radius and the center point are. "/>
+ * </p>
*
* <p>Note: The rounded corner formed by the radius and the center is an approximation.</p>
*
diff --git a/core/java/android/view/SoundEffectConstants.java b/core/java/android/view/SoundEffectConstants.java
index f177451..bd86a47 100644
--- a/core/java/android/view/SoundEffectConstants.java
+++ b/core/java/android/view/SoundEffectConstants.java
@@ -16,11 +16,14 @@
package android.view;
+import android.annotation.IntDef;
import android.media.AudioManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Random;
/**
@@ -34,25 +37,55 @@
public static final int CLICK = 0;
+ /** Effect id for a navigation left */
public static final int NAVIGATION_LEFT = 1;
+ /** Effect id for a navigation up */
public static final int NAVIGATION_UP = 2;
+ /** Effect id for a navigation right */
public static final int NAVIGATION_RIGHT = 3;
+ /** Effect id for a navigation down */
public static final int NAVIGATION_DOWN = 4;
- /** Sound effect for a repeatedly triggered navigation, e.g. due to long pressing a button */
+ /** Effect id for a repeatedly triggered navigation left, e.g. due to long pressing a button */
public static final int NAVIGATION_REPEAT_LEFT = 5;
- /** @see #NAVIGATION_REPEAT_LEFT */
+ /** Effect id for a repeatedly triggered navigation up, e.g. due to long pressing a button */
public static final int NAVIGATION_REPEAT_UP = 6;
- /** @see #NAVIGATION_REPEAT_LEFT */
+ /** Effect id for a repeatedly triggered navigation right, e.g. due to long pressing a button */
public static final int NAVIGATION_REPEAT_RIGHT = 7;
- /** @see #NAVIGATION_REPEAT_LEFT */
+ /** Effect id for a repeatedly triggered navigation down, e.g. due to long pressing a button */
public static final int NAVIGATION_REPEAT_DOWN = 8;
+ /** @hide */
+ @IntDef(value = {
+ CLICK,
+ NAVIGATION_LEFT,
+ NAVIGATION_UP,
+ NAVIGATION_RIGHT,
+ NAVIGATION_DOWN,
+ NAVIGATION_REPEAT_LEFT,
+ NAVIGATION_REPEAT_UP,
+ NAVIGATION_REPEAT_RIGHT,
+ NAVIGATION_REPEAT_DOWN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SoundEffect {}
+
+ /** @hide */
+ @IntDef(prefix = { "NAVIGATION_" }, value = {
+ NAVIGATION_LEFT,
+ NAVIGATION_UP,
+ NAVIGATION_RIGHT,
+ NAVIGATION_DOWN,
+ NAVIGATION_REPEAT_LEFT,
+ NAVIGATION_REPEAT_UP,
+ NAVIGATION_REPEAT_RIGHT,
+ NAVIGATION_REPEAT_DOWN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NavigationSoundEffect {}
+
/**
* Get the sonification constant for the focus directions.
- * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
- * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD}
- * or {@link View#FOCUS_BACKWARD}
-
+ * @param direction The direction of the focus.
* @return The appropriate sonification constant.
* @throws {@link IllegalArgumentException} when the passed direction is not one of the
* documented values.
@@ -76,16 +109,14 @@
/**
* Get the sonification constant for the focus directions
- * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
- * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD}
- * or {@link View#FOCUS_BACKWARD}
+ * @param direction The direction of the focus.
* @param repeating True if the user long-presses a direction
* @return The appropriate sonification constant
* @throws IllegalArgumentException when the passed direction is not one of the
* documented values.
*/
- public static int getConstantForFocusDirection(@View.FocusDirection int direction,
- boolean repeating) {
+ public static @NavigationSoundEffect int getConstantForFocusDirection(
+ @View.FocusDirection int direction, boolean repeating) {
if (repeating) {
switch (direction) {
case View.FOCUS_RIGHT:
@@ -112,7 +143,7 @@
* @hide
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
- public static boolean isNavigationRepeat(int effectId) {
+ public static boolean isNavigationRepeat(@NavigationSoundEffect int effectId) {
return effectId == SoundEffectConstants.NAVIGATION_REPEAT_DOWN
|| effectId == SoundEffectConstants.NAVIGATION_REPEAT_LEFT
|| effectId == SoundEffectConstants.NAVIGATION_REPEAT_RIGHT
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 85d4878..b2a84a2 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -190,6 +190,8 @@
private static native void nativeReparent(long transactionObj, long nativeObject,
long newParentNativeObject);
+ private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
+
private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
InputWindowHandle handle);
@@ -2204,6 +2206,18 @@
}
/**
+ * Overrides HDR modes for a display device.
+ *
+ * If the caller does not have ACCESS_SURFACE_FLINGER permission, this will throw a Security
+ * Exception.
+ * @hide
+ */
+ @TestApi
+ public static void overrideHdrTypes(@NonNull IBinder displayToken, @NonNull int[] modes) {
+ nativeOverrideHdrTypes(displayToken, modes);
+ }
+
+ /**
* @hide
*/
@UnsupportedAppUsage
@@ -2244,6 +2258,8 @@
*
* @hide
*/
+ @TestApi
+ @NonNull
public static IBinder getInternalDisplayToken() {
final long[] physicalDisplayIds = getPhysicalDisplayIds();
if (physicalDisplayIds.length == 0) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7455b8b..63a7dea 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -107,6 +107,7 @@
import android.util.FloatProperty;
import android.util.LayoutDirection;
import android.util.Log;
+import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.Pair;
import android.util.Pools.SynchronizedPool;
@@ -151,6 +152,9 @@
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
import android.view.inspector.InspectableProperty.FlagEntry;
+import android.view.translation.TranslationCapability;
+import android.view.translation.TranslationSpec.DataFormat;
+import android.view.translation.ViewTranslationCallback;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
import android.widget.Checkable;
@@ -5253,6 +5257,9 @@
@Nullable
private String[] mOnReceiveContentMimeTypes;
+ @Nullable
+ private ViewTranslationCallback mViewTranslationCallback;
+
/**
* Simple constructor to use when creating a view from code.
*
@@ -26136,9 +26143,9 @@
* <p>The sound effect will only be played if sound effects are enabled by the user, and
* {@link #isSoundEffectsEnabled()} is true.
*
- * @param soundConstant One of the constants defined in {@link SoundEffectConstants}
+ * @param soundConstant One of the constants defined in {@link SoundEffectConstants}.
*/
- public void playSoundEffect(int soundConstant) {
+ public void playSoundEffect(@SoundEffectConstants.SoundEffect int soundConstant) {
if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {
return;
}
@@ -26794,8 +26801,10 @@
if (permissions != null) {
permissions.takeTransient();
}
- final ContentInfo payload = new ContentInfo.Builder(
- event.getClipData(), SOURCE_DRAG_AND_DROP).build();
+ final ContentInfo payload =
+ new ContentInfo.Builder(event.getClipData(), SOURCE_DRAG_AND_DROP)
+ .setDragAndDropPermissions(permissions)
+ .build();
ContentInfo remainingPayload = performReceiveContent(payload);
// Return true unless none of the payload was consumed.
return remainingPayload != payload;
@@ -30718,71 +30727,141 @@
}
/**
- * Returns a {@link ViewTranslationRequest} to the {@link onStartUiTranslation} which represents
- * the content to be translated.
+ * Returns a {@link ViewTranslationRequest} which represents the content to be translated.
*
- * <p>The default implementation does nothing and return null.</p>
+ * <p>The default implementation does nothing and returns null.</p>
*
- * @hide
- *
- * @return the {@link ViewTranslationRequest} which contains the information to be translated.
+ * @param supportedFormats the supported translation formats. For now, the only possible value
+ * is the {@link android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}.
+ * @return the {@link ViewTranslationRequest} which contains the information to be translated or
+ * {@code null} if this View doesn't support translation.
+ * The {@link AutofillId} must be set on the returned value.
*/
@Nullable
- //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed.
- public ViewTranslationRequest onCreateTranslationRequest() {
+ public ViewTranslationRequest onCreateTranslationRequest(
+ @NonNull @DataFormat int[] supportedFormats) {
return null;
}
/**
- * Called when the user wants to show the original text instead of the translated text.
+ * Returns a {@link ViewTranslationRequest} list which represents the content to be translated
+ * in the virtual view. This is called if this view returned a virtual view structure
+ * from {@link #onProvideContentCaptureStructure} and the system determined that those virtual
+ * views were relevant for translation.
*
- * @hide
+ * <p>The default implementation does nothing.</p>
*
- * <p> The default implementation does nothing.
+ * @param virtualChildIds the virtual child ids which represents the child views in the virtual
+ * view.
+ * @param supportedFormats the supported translation formats. For now, the only possible value
+ * is the {@link android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}.
+ * @param requestsCollector a {@link ViewTranslationRequest} collector that will be called
+ * multiple times to collect the information to be translated in the virtual view. One
+ * {@link ViewTranslationRequest} per virtual child. The {@link ViewTranslationRequest} must
+ * contains the {@link AutofillId} corresponding to the virtualChildIds.
*/
- //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed.
- public void onPauseUiTranslation() {
+ @SuppressLint("NullableCollection")
+ public void onCreateTranslationRequests(@NonNull long[] virtualChildIds,
+ @NonNull @DataFormat int[] supportedFormats,
+ @NonNull Consumer<ViewTranslationRequest> requestsCollector) {
// no-op
}
/**
- * User can switch back to show the original text, this method called when the user wants to
- * re-show the translated text again.
+ * Returns a {@link ViewTranslationCallback} that is used to display/hide the translated
+ * information. If the View supports displaying translated content, it should implement
+ * {@link ViewTranslationCallback}.
*
- * @hide
+ * <p>The default implementation returns null if developers don't set the customized
+ * {@link ViewTranslationCallback} by {@link #setViewTranslationCallback} </p>
+ *
+ * @return a {@link ViewTranslationCallback} that is used to control how to display the
+ * translated information or {@code null} if this View doesn't support translation.
+ */
+ @Nullable
+ public ViewTranslationCallback getViewTranslationCallback() {
+ return mViewTranslationCallback;
+ }
+
+ /**
+ * Sets a {@link ViewTranslationCallback} that is used to display/hide the translated
+ * information. Developers can provide the customized implementation for show/hide translated
+ * information.
+ *
+ * @param callback a {@link ViewTranslationCallback} that is used to control how to display the
+ * translated information
+ */
+ public void setViewTranslationCallback(@NonNull ViewTranslationCallback callback) {
+ mViewTranslationCallback = callback;
+ }
+
+ /**
+ * Called when the content from {@link View#onCreateTranslationRequest} had been translated by
+ * the TranslationService.
*
* <p> The default implementation does nothing.</p>
+ *
+ * @param response a {@link ViewTranslationResponse} that contains the translated information
+ * which can be shown in the view.
*/
- //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed.
- public void onRestoreUiTranslation() {
+ public void onTranslationResponse(@NonNull ViewTranslationResponse response) {
// no-op
}
/**
- * Called when the user finish the Ui translation and no longer to show the translated text.
- *
- * @hide
+ * Called when the content from {@link View#onCreateTranslationRequest} had been translated by
+ * the TranslationService.
*
* <p> The default implementation does nothing.</p>
+ *
+ * @param response a {@link ViewTranslationResponse} SparseArray for the request that send by
+ * {@link View#onCreateTranslationRequests} that contains the translated information which can
+ * be shown in the view. The key of SparseArray is
+ * the virtual child ids.
*/
- //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed.
- public void onFinishUiTranslation() {
+ public void onTranslationResponse(@NonNull LongSparseArray<ViewTranslationResponse> response) {
// no-op
}
/**
- * Called when the request from {@link onStartUiTranslation} is completed by the translation
- * service so that the translation result can be shown.
+ * Dispatch to collect the {@link ViewTranslationRequest}s for translation purpose by traversing
+ * the hierarchy when the app requests ui translation. Typically, this method should only be
+ * overridden by subclasses that provide a view hierarchy (such as {@link ViewGroup}). Other
+ * classes should override {@link View#onCreateTranslationRequest}. When requested to start the
+ * ui translation, the system will call this method to traverse the view hierarchy to call
+ * {@link View#onCreateTranslationRequest} to build {@link ViewTranslationRequest}s and create a
+ * {@link android.view.translation.Translator} to translate the requests. All the
+ * {@link ViewTranslationRequest}s will be added when the traversal is done.
*
- * @hide
+ * <p> The default implementation will call {@link View#onCreateTranslationRequest} to build
+ * {@link ViewTranslationRequest} if the view should be translated. </p>
*
- * <p> The default implementation does nothing.</p>
- *
- * @param response the translated information which can be shown in the view.
+ * @param viewIds a map for the view's {@link AutofillId} and its virtual child ids or
+ * {@code null} if the view doesn't have virtual child that should be translated. The virtual
+ * child ids are the same virtual ids provided by ContentCapture.
+ * @param supportedFormats the supported translation formats. For now, the only possible value
+ * is the {@link android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}.
+ * @param capability a {@link TranslationCapability} that holds translation capability.
+ * information, e.g. source spec, target spec.
+ * @param requests fill in with {@link ViewTranslationRequest}s for translation purpose.
*/
- //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed.
- public void onTranslationComplete(@NonNull ViewTranslationResponse response) {
- // no-op
+ public void dispatchRequestTranslation(@NonNull Map<AutofillId, long[]> viewIds,
+ @NonNull @DataFormat int[] supportedFormats,
+ @Nullable TranslationCapability capability,
+ @NonNull List<ViewTranslationRequest> requests) {
+ AutofillId autofillId = getAutofillId();
+ if (viewIds.containsKey(autofillId)) {
+ if (viewIds.get(autofillId) == null) {
+ ViewTranslationRequest request = onCreateTranslationRequest(supportedFormats);
+ if (request != null && request.getKeys().size() > 0) {
+ requests.add(request);
+ }
+ } else {
+ onCreateTranslationRequests(viewIds.get(autofillId), supportedFormats, request -> {
+ requests.add(request);
+ });
+ }
+ }
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 38a5937..8198254 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -65,9 +65,13 @@
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.view.animation.Transformation;
+import android.view.autofill.AutofillId;
import android.view.autofill.Helper;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
+import android.view.translation.TranslationCapability;
+import android.view.translation.TranslationSpec.DataFormat;
+import android.view.translation.ViewTranslationRequest;
import com.android.internal.R;
@@ -9264,4 +9268,25 @@
mParent.onDescendantUnbufferedRequested();
}
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * The implementation calls {@link #dispatchRequestTranslation} for all the child views.
+ */
+ @Override
+ public void dispatchRequestTranslation(@NonNull Map<AutofillId, long[]> viewIds,
+ @NonNull @DataFormat int[] supportedFormats,
+ @Nullable TranslationCapability capability,
+ @NonNull List<ViewTranslationRequest> requests) {
+ super.dispatchRequestTranslation(viewIds, supportedFormats, capability, requests);
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ return;
+ }
+ for (int i = 0; i < childCount; ++i) {
+ final View child = getChildAt(i);
+ child.dispatchRequestTranslation(viewIds, supportedFormats, capability, requests);
+ }
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index dbccf10..426c950 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3344,8 +3344,9 @@
}
// TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
// config changes.
+ final View focusedView = mView != null ? mView.findFocus() : null;
if (hasWindowFocus) {
- mInsetsController.onWindowFocusGained();
+ mInsetsController.onWindowFocusGained(focusedView != null /* hasViewFocused */);
} else {
mInsetsController.onWindowFocusLost();
}
@@ -3394,8 +3395,7 @@
// Note: must be done after the focus change callbacks,
// so all of the view state is set up correctly.
- mImeFocusController.onPostWindowFocus(mView != null ? mView.findFocus() : null,
- hasWindowFocus, mWindowAttributes);
+ mImeFocusController.onPostWindowFocus(focusedView, hasWindowFocus, mWindowAttributes);
if (hasWindowFocus) {
// Clear the forward bit. We can just do this directly, since
@@ -4293,7 +4293,7 @@
mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
boolean useAsyncReport = false;
- if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
+ if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty || mNextDrawUseBlastSync) {
if (isHardwareEnabled()) {
// If accessibility focus moved, always invalidate the root.
boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
@@ -7754,7 +7754,7 @@
* {@inheritDoc}
*/
@Override
- public void playSoundEffect(int effectId) {
+ public void playSoundEffect(@SoundEffectConstants.SoundEffect int effectId) {
checkThread();
try {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index b0b9d24..a169cb0 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1921,20 +1921,20 @@
if (client == null) return; // NOTE: getClient() already logged it..
final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
- final ComponentName componentName = client.autofillClientGetComponentName();
+ final ComponentName clientActivity = client.autofillClientGetComponentName();
if (!mEnabledForAugmentedAutofillOnly && mOptions != null
- && mOptions.isAutofillDisabledLocked(componentName)) {
+ && mOptions.isAutofillDisabledLocked(clientActivity)) {
if (mOptions.isAugmentedAutofillEnabled(mContext)) {
if (sDebug) {
- Log.d(TAG, "startSession(" + componentName + "): disabled by service but "
- + "whitelisted for augmented autofill");
+ Log.d(TAG, "startSession(" + clientActivity + "): disabled by service but "
+ + "allowlisted for augmented autofill");
flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
}
} else {
if (sDebug) {
- Log.d(TAG, "startSession(" + componentName + "): ignored because "
- + "disabled by service and not whitelisted for augmented autofill");
+ Log.d(TAG, "startSession(" + clientActivity + "): ignored because "
+ + "disabled by service and not allowlisted for augmented autofill");
}
setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, null);
client.autofillClientResetableStateAvailable();
@@ -1951,7 +1951,7 @@
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
- mCallback != null, flags, componentName,
+ mCallback != null, flags, clientActivity,
isCompatibilityModeEnabledLocked(), receiver);
mSessionId = receiver.getIntResult();
if (mSessionId != NO_SESSION) {
@@ -1959,7 +1959,7 @@
}
final int extraFlags = receiver.getOptionalExtraIntResult(0);
if ((extraFlags & RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) != 0) {
- if (sDebug) Log.d(TAG, "startSession(" + componentName + "): for augmented only");
+ if (sDebug) Log.d(TAG, "startSession(" + clientActivity + "): for augmented only");
mForAugmentedAutofillOnly = true;
}
client.autofillClientResetableStateAvailable();
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 2b12230..ce01469 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -143,6 +143,9 @@
private @Nullable ContentCaptureContext mClientContext;
private @Nullable Insets mInsets;
+ /** Only used in the main Content Capture session, no need to parcel */
+ private boolean mTextHasComposingSpan;
+
/** @hide */
public ContentCaptureEvent(int sessionId, int type, long eventTime) {
mSessionId = sessionId;
@@ -243,11 +246,21 @@
/** @hide */
@NonNull
- public ContentCaptureEvent setText(@Nullable CharSequence text) {
+ public ContentCaptureEvent setText(@Nullable CharSequence text, boolean hasComposingSpan) {
mText = text;
+ mTextHasComposingSpan = hasComposingSpan;
return this;
}
+ /**
+ * The value is not parcelled, become false after parcelled.
+ * @hide
+ */
+ @NonNull
+ public boolean getTextHasComposingSpan() {
+ return mTextHasComposingSpan;
+ }
+
/** @hide */
@NonNull
public ContentCaptureEvent setInsets(@NonNull Insets insets) {
@@ -361,7 +374,7 @@
throw new IllegalArgumentException("mergeEvent(): got "
+ "TYPE_VIEW_DISAPPEARED event with neither id or ids: " + event);
} else if (eventType == TYPE_VIEW_TEXT_CHANGED) {
- setText(event.getText());
+ setText(event.getText(), event.getTextHasComposingSpan());
} else {
Log.e(TAG, "mergeEvent(" + getTypeAsString(eventType)
+ ") does not support this event type.");
@@ -479,7 +492,7 @@
if (node != null) {
event.setViewNode(node);
}
- event.setText(parcel.readCharSequence());
+ event.setText(parcel.readCharSequence(), false);
if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
event.setParentSessionId(parcel.readInt());
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 5ca793e..f196f75 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -43,12 +43,15 @@
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
+import android.text.Spannable;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.Log;
import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import android.view.inputmethod.BaseInputConnection;
import com.android.internal.os.IResultReceiver;
@@ -57,6 +60,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -147,6 +151,12 @@
private final LocalLog mFlushHistory;
/**
+ * If the event in the buffer is of type {@link TYPE_VIEW_TEXT_CHANGED}, this value
+ * indicates whether the event has composing span or not.
+ */
+ private final Map<AutofillId, Boolean> mLastComposingSpan = new ArrayMap<>();
+
+ /**
* Binder object used to update the session state.
*/
@NonNull
@@ -335,26 +345,47 @@
// Some type of events can be merged together
boolean addEvent = true;
- if (!mEvents.isEmpty() && eventType == TYPE_VIEW_TEXT_CHANGED) {
- final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
+ if (eventType == TYPE_VIEW_TEXT_CHANGED) {
+ // We determine whether to add or merge the current event by following criteria:
+ // 1. Don't have composing span: always add.
+ // 2. Have composing span:
+ // 2.1 either last or current text is empty: add.
+ // 2.2 last event doesn't have composing span: add.
+ // Otherwise, merge.
- // We merge two consecutive text change event, unless one of them clears the text.
- if (lastEvent.getType() == TYPE_VIEW_TEXT_CHANGED
- && lastEvent.getId().equals(event.getId())) {
- boolean bothNonEmpty = !TextUtils.isEmpty(lastEvent.getText())
- && !TextUtils.isEmpty(event.getText());
- boolean equalContent = TextUtils.equals(lastEvent.getText(), event.getText());
- if (equalContent) {
- addEvent = false;
- } else if (bothNonEmpty) {
- lastEvent.mergeEvent(event);
- addEvent = false;
- }
- if (!addEvent && sVerbose) {
- Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
- + getSanitizedString(event.getText()));
+ final CharSequence text = event.getText();
+ final boolean textHasComposingSpan = event.getTextHasComposingSpan();
+
+ if (textHasComposingSpan && !mLastComposingSpan.isEmpty()) {
+ final Boolean lastEventHasComposingSpan = mLastComposingSpan.get(event.getId());
+ if (lastEventHasComposingSpan != null && lastEventHasComposingSpan.booleanValue()) {
+ ContentCaptureEvent lastEvent = null;
+ for (int index = mEvents.size() - 1; index >= 0; index--) {
+ final ContentCaptureEvent tmpEvent = mEvents.get(index);
+ if (event.getId().equals(tmpEvent.getId())) {
+ lastEvent = tmpEvent;
+ break;
+ }
+ }
+ if (lastEvent != null) {
+ final CharSequence lastText = lastEvent.getText();
+ final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
+ && !TextUtils.isEmpty(text);
+ boolean equalContent = TextUtils.equals(lastText, text);
+ if (equalContent) {
+ addEvent = false;
+ } else if (bothNonEmpty && lastEventHasComposingSpan) {
+ lastEvent.mergeEvent(event);
+ addEvent = false;
+ }
+ if (!addEvent && sVerbose) {
+ Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
+ + getSanitizedString(text));
+ }
+ }
}
}
+ mLastComposingSpan.put(event.getId(), textHasComposingSpan);
}
if (!mEvents.isEmpty() && eventType == TYPE_VIEW_DISAPPEARED) {
@@ -374,6 +405,11 @@
mEvents.add(event);
}
+ // TODO: we need to change when the flush happens so that we don't flush while the
+ // composing span hasn't changed. But we might need to keep flushing the events for the
+ // non-editable views and views that don't have the composing state; otherwise some other
+ // Content Capture features may be delayed.
+
final int numberEvents = mEvents.size();
final boolean bufferEvent = numberEvents < maxBufferSize;
@@ -550,6 +586,7 @@
? Collections.emptyList()
: mEvents;
mEvents = null;
+ mLastComposingSpan.clear();
return new ParceledListSlice<>(events);
}
@@ -677,9 +714,16 @@
}
void notifyViewTextChanged(int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
+ // Since the same CharSequence instance may be reused in the TextView, we need to make
+ // a copy of its content so that its value will not be changed by subsequent updates
+ // in the TextView.
+ final String eventText = text == null ? null : text.toString();
+ final boolean textHasComposingSpan =
+ text instanceof Spannable && BaseInputConnection.getComposingSpanStart(
+ (Spannable) text) >= 0;
mHandler.post(() -> sendEvent(
new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)
- .setAutofillId(id).setText(text)));
+ .setAutofillId(id).setText(eventText, textHasComposingSpan)));
}
/** Public because is also used by ViewRootImpl */
diff --git a/core/java/android/view/displayhash/DisplayHashManager.java b/core/java/android/view/displayhash/DisplayHashManager.java
index 6b0c1a6..69dfc38 100644
--- a/core/java/android/view/displayhash/DisplayHashManager.java
+++ b/core/java/android/view/displayhash/DisplayHashManager.java
@@ -21,7 +21,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
@@ -94,4 +96,19 @@
return null;
}
}
+
+ /**
+ * Call to enable or disable the throttling when generating a display hash. This should only be
+ * used for testing. Throttling is enabled by default.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.READ_FRAME_BUFFER)
+ public void setDisplayHashThrottlingEnabled(boolean enable) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().setDisplayHashThrottlingEnabled(enable);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/view/displayhash/DisplayHashResultCallback.java b/core/java/android/view/displayhash/DisplayHashResultCallback.java
index 04d29ee..6e3d9a8 100644
--- a/core/java/android/view/displayhash/DisplayHashResultCallback.java
+++ b/core/java/android/view/displayhash/DisplayHashResultCallback.java
@@ -72,13 +72,20 @@
*/
int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5;
+ /**
+ * The caller requested to generate the hash too frequently. The caller should try again at a
+ * after some time has passed to ensure the system isn't overloaded.
+ */
+ int DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS = -6;
+
/** @hide */
@IntDef(prefix = {"DISPLAY_HASH_ERROR_"}, value = {
DISPLAY_HASH_ERROR_UNKNOWN,
DISPLAY_HASH_ERROR_INVALID_BOUNDS,
DISPLAY_HASH_ERROR_MISSING_WINDOW,
DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN,
- DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM
+ DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM,
+ DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS
})
@Retention(RetentionPolicy.SOURCE)
@interface DisplayHashErrorCode {
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index c5bce28..9f796c0 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -959,10 +959,10 @@
}
final ClipData clip = new ClipData(inputContentInfo.getDescription(),
new ClipData.Item(inputContentInfo.getContentUri()));
- final ContentInfo payload =
- new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD)
+ final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD)
.setLinkUri(inputContentInfo.getLinkUri())
.setExtras(opts)
+ .setInputContentInfo(inputContentInfo)
.build();
return mTargetView.performReceiveContent(payload) == null;
}
diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java
index dfa7095..b89488b 100644
--- a/core/java/android/view/translation/TranslationManager.java
+++ b/core/java/android/view/translation/TranslationManager.java
@@ -121,17 +121,16 @@
}
/**
- * Create a Translator for translation.
+ * Creates an on-device Translator for natural language translation.
*
* <p><strong>NOTE: </strong>Call on a worker thread.
*
* @param translationContext {@link TranslationContext} containing the specs for creating the
* Translator.
- * @return a {@link Translator} to be used for calling translation APIs.
*/
@Nullable
@WorkerThread
- public Translator createTranslator(@NonNull TranslationContext translationContext) {
+ public Translator createOnDeviceTranslator(@NonNull TranslationContext translationContext) {
Objects.requireNonNull(translationContext, "translationContext cannot be null");
synchronized (mLock) {
@@ -166,8 +165,16 @@
}
}
+ /** @deprecated Use {@link #createOnDeviceTranslator(TranslationContext)} */
+ @Deprecated
+ @Nullable
+ @WorkerThread
+ public Translator createTranslator(@NonNull TranslationContext translationContext) {
+ return createOnDeviceTranslator(translationContext);
+ }
+
/**
- * Returns a set of {@link TranslationCapability}s describing the capabilities for
+ * Returns a set of {@link TranslationCapability}s describing the capabilities for on-device
* {@link Translator}s.
*
* <p>These translation capabilities contains a source and target {@link TranslationSpec}
@@ -184,7 +191,7 @@
*/
@NonNull
@WorkerThread
- public Set<TranslationCapability> getTranslationCapabilities(
+ public Set<TranslationCapability> getOnDeviceTranslationCapabilities(
@TranslationSpec.DataFormat int sourceFormat,
@TranslationSpec.DataFormat int targetFormat) {
try {
@@ -206,8 +213,18 @@
}
}
+ /** @deprecated Use {@link #getOnDeviceTranslationCapabilities(int, int)} */
+ @Deprecated
+ @NonNull
+ @WorkerThread
+ public Set<TranslationCapability> getTranslationCapabilities(
+ @TranslationSpec.DataFormat int sourceFormat,
+ @TranslationSpec.DataFormat int targetFormat) {
+ return getOnDeviceTranslationCapabilities(sourceFormat, targetFormat);
+ }
+
/**
- * Registers a {@link PendingIntent} to listen for updates on states of
+ * Registers a {@link PendingIntent} to listen for updates on states of on-device
* {@link TranslationCapability}s.
*
* <p>IMPORTANT: the pending intent must be called to start a service, or a broadcast if it is
@@ -217,7 +234,7 @@
* @param targetFormat data format for the expected translated output data.
* @param pendingIntent the pending intent to invoke when updates are received.
*/
- public void addTranslationCapabilityUpdateListener(
+ public void addOnDeviceTranslationCapabilityUpdateListener(
@TranslationSpec.DataFormat int sourceFormat,
@TranslationSpec.DataFormat int targetFormat,
@NonNull PendingIntent pendingIntent) {
@@ -231,14 +248,26 @@
}
/**
- * Unregisters a {@link PendingIntent} to listen for updates on states of
+ * @deprecated Use {@link #addOnDeviceTranslationCapabilityUpdateListener(int, int,
+ * PendingIntent)}
+ */
+ @Deprecated
+ public void addTranslationCapabilityUpdateListener(
+ @TranslationSpec.DataFormat int sourceFormat,
+ @TranslationSpec.DataFormat int targetFormat,
+ @NonNull PendingIntent pendingIntent) {
+ addOnDeviceTranslationCapabilityUpdateListener(sourceFormat, targetFormat, pendingIntent);
+ }
+
+ /**
+ * Unregisters a {@link PendingIntent} to listen for updates on states of on-device
* {@link TranslationCapability}s.
*
* @param sourceFormat data format for the input data to be translated.
* @param targetFormat data format for the expected translated output data.
* @param pendingIntent the pending intent to unregister
*/
- public void removeTranslationCapabilityUpdateListener(
+ public void removeOnDeviceTranslationCapabilityUpdateListener(
@TranslationSpec.DataFormat int sourceFormat,
@TranslationSpec.DataFormat int targetFormat,
@NonNull PendingIntent pendingIntent) {
@@ -262,10 +291,24 @@
}
}
+ /**
+ * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener(int, int,
+ * PendingIntent)}
+ */
+ @Deprecated
+ public void removeTranslationCapabilityUpdateListener(
+ @TranslationSpec.DataFormat int sourceFormat,
+ @TranslationSpec.DataFormat int targetFormat,
+ @NonNull PendingIntent pendingIntent) {
+ removeOnDeviceTranslationCapabilityUpdateListener(
+ sourceFormat, targetFormat, pendingIntent);
+ }
+
//TODO: Add method to propagate updates to mTCapabilityUpdateListeners
/**
- * Returns an immutable PendingIntent which can used by apps to launch translation settings.
+ * Returns an immutable PendingIntent which can be used to launch an activity to view/edit
+ * on-device translation settings.
*
* @return An immutable PendingIntent or {@code null} if one of reason met:
* <ul>
@@ -274,7 +317,7 @@
* </ul>
**/
@Nullable
- public PendingIntent getTranslationSettingsActivityIntent() {
+ public PendingIntent getOnDeviceTranslationSettingsActivityIntent() {
final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.getServiceSettingsActivity(resultReceiver, mContext.getUserId());
@@ -289,6 +332,13 @@
}
}
+ /** @deprecated Use {@link #getOnDeviceTranslationSettingsActivityIntent()} */
+ @Deprecated
+ @Nullable
+ public PendingIntent getTranslationSettingsActivityIntent() {
+ return getOnDeviceTranslationSettingsActivityIntent();
+ }
+
void removeTranslator(int id) {
synchronized (mLock) {
mTranslators.remove(id);
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index d79ecca..0fa6e16 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -31,8 +31,10 @@
import android.os.Process;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.LongSparseArray;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
@@ -46,7 +48,8 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.Consumer;
+import java.util.Map;
+import java.util.function.BiConsumer;
/**
* A controller to manage the ui translation requests for the {@link Activity}.
@@ -77,6 +80,7 @@
private final HandlerThread mWorkerThread;
@NonNull
private final Handler mWorkerHandler;
+ private int mCurrentState;
public UiTranslationController(Activity activity, Context context) {
mActivity = activity;
@@ -101,6 +105,9 @@
}
Log.i(TAG, "updateUiTranslationState state: " + stateToString(state)
+ (DEBUG ? ", views: " + views : ""));
+ synchronized (mLock) {
+ mCurrentState = state;
+ }
switch (state) {
case STATE_UI_TRANSLATION_STARTED:
final Pair<TranslationSpec, TranslationSpec> specs =
@@ -114,14 +121,14 @@
}
break;
case STATE_UI_TRANSLATION_PAUSED:
- runForEachView(View::onPauseUiTranslation);
+ runForEachView((view, callback) -> callback.onHideTranslation(view));
break;
case STATE_UI_TRANSLATION_RESUMED:
- runForEachView(View::onRestoreUiTranslation);
+ runForEachView((view, callback) -> callback.onShowTranslation(view));
break;
case STATE_UI_TRANSLATION_FINISHED:
destroyTranslators();
- runForEachView(View::onFinishUiTranslation);
+ runForEachView((view, callback) -> callback.onClearTranslation(view));
synchronized (mLock) {
mViews.clear();
}
@@ -219,7 +226,6 @@
pw.print(outerPrefix); pw.print("isContainsView: "); pw.println(isContainsView);
}
-
/**
* The method is used by {@link Translator}, it will be called when the translation is done. The
* translation result can be get from here.
@@ -232,11 +238,95 @@
}
final SparseArray<ViewTranslationResponse> translatedResult =
response.getViewTranslationResponses();
- onTranslationCompleted(translatedResult);
+ final SparseArray<ViewTranslationResponse> viewsResult = new SparseArray<>();
+ final SparseArray<LongSparseArray<ViewTranslationResponse>> virtualViewsResult =
+ new SparseArray<>();
+ // TODO: use another structure to prevent autoboxing?
+ final List<Integer> viewIds = new ArrayList<>();
+
+ for (int i = 0; i < translatedResult.size(); i++) {
+ final ViewTranslationResponse result = translatedResult.valueAt(i);
+ final AutofillId autofillId = result.getAutofillId();
+ if (!viewIds.contains(autofillId.getViewId())) {
+ viewIds.add(autofillId.getViewId());
+ }
+ if (autofillId.isNonVirtual()) {
+ viewsResult.put(translatedResult.keyAt(i), result);
+ } else {
+ final boolean isVirtualViewAdded =
+ virtualViewsResult.indexOfKey(autofillId.getViewId()) >= 0;
+ final LongSparseArray<ViewTranslationResponse> childIds =
+ isVirtualViewAdded ? virtualViewsResult.get(autofillId.getViewId())
+ : new LongSparseArray<>();
+ childIds.put(autofillId.getVirtualChildLongId(), result);
+ if (!isVirtualViewAdded) {
+ virtualViewsResult.put(autofillId.getViewId(), childIds);
+ }
+ }
+ }
+ // Traverse tree and get views by the responsed AutofillId
+ findViewsTraversalByAutofillIds(viewIds);
+
+ if (viewsResult.size() > 0) {
+ onTranslationCompleted(viewsResult);
+ }
+ if (virtualViewsResult.size() > 0) {
+ onVirtualViewTranslationCompleted(virtualViewsResult);
+ }
}
+ /**
+ * The method is used to handle the translation result for the vertual views.
+ */
+ private void onVirtualViewTranslationCompleted(
+ SparseArray<LongSparseArray<ViewTranslationResponse>> translatedResult) {
+ if (!mActivity.isResumed()) {
+ if (DEBUG) {
+ Log.v(TAG, "onTranslationCompleted: Activity is not resumed.");
+ }
+ return;
+ }
+ synchronized (mLock) {
+ if (mCurrentState == STATE_UI_TRANSLATION_FINISHED) {
+ Log.w(TAG, "onTranslationCompleted: the translation state is finished now. "
+ + "Skip to show the translated text.");
+ return;
+ }
+ for (int i = 0; i < translatedResult.size(); i++) {
+ final AutofillId autofillId = new AutofillId(translatedResult.keyAt(i));
+ final View view = mViews.get(autofillId).get();
+ if (view == null) {
+ Log.w(TAG, "onTranslationCompleted: the view for autofill id " + autofillId
+ + " may be gone.");
+ continue;
+ }
+ final LongSparseArray<ViewTranslationResponse> virtualChildResponse =
+ translatedResult.valueAt(i);
+ mActivity.runOnUiThread(() -> {
+ if (view.getViewTranslationCallback() == null) {
+ if (DEBUG) {
+ Log.d(TAG, view + " doesn't support showing translation because of "
+ + "null ViewTranslationCallback.");
+ }
+ return;
+ }
+ view.onTranslationResponse(virtualChildResponse);
+ if (view.getViewTranslationCallback() != null) {
+ view.getViewTranslationCallback().onShowTranslation(view);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * The method is used to handle the translation result for non-vertual views.
+ */
private void onTranslationCompleted(SparseArray<ViewTranslationResponse> translatedResult) {
if (!mActivity.isResumed()) {
+ if (DEBUG) {
+ Log.v(TAG, "onTranslationCompleted: Activity is not resumed.");
+ }
return;
}
final int resultCount = translatedResult.size();
@@ -244,8 +334,13 @@
Log.v(TAG, "onTranslationCompleted: receive " + resultCount + " responses.");
}
synchronized (mLock) {
+ if (mCurrentState == STATE_UI_TRANSLATION_FINISHED) {
+ Log.w(TAG, "onTranslationCompleted: the translation state is finished now. "
+ + "Skip to show the translated text.");
+ return;
+ }
for (int i = 0; i < resultCount; i++) {
- final ViewTranslationResponse response = translatedResult.get(i);
+ final ViewTranslationResponse response = translatedResult.valueAt(i);
final AutofillId autofillId = response.getAutofillId();
if (autofillId == null) {
continue;
@@ -256,18 +351,30 @@
+ " may be gone.");
continue;
}
- mActivity.runOnUiThread(() -> view.onTranslationComplete(response));
+ mActivity.runOnUiThread(() -> {
+ if (view.getViewTranslationCallback() == null) {
+ if (DEBUG) {
+ Log.d(TAG, view + " doesn't support showing translation because of "
+ + "null ViewTranslationCallback.");
+ }
+ return;
+ }
+ view.onTranslationResponse(response);
+ if (view.getViewTranslationCallback() != null) {
+ view.getViewTranslationCallback().onShowTranslation(view);
+ }
+ });
}
}
}
/**
- * Called when there is an ui translation request comes to request view translation.
+ * Creates a Translator for the given source and target translation specs and start the ui
+ * translation when the Translator is created successfully.
*/
@WorkerThread
private void createTranslatorAndStart(TranslationSpec sourceSpec, TranslationSpec destSpec,
List<AutofillId> views) {
- // Create Translator
final Translator translator = createTranslatorIfNeeded(sourceSpec, destSpec);
if (translator == null) {
Log.w(TAG, "Can not create Translator for sourceSpec:" + sourceSpec + " destSpec:"
@@ -295,81 +402,117 @@
*/
private void onUiTranslationStarted(Translator translator, List<AutofillId> views) {
synchronized (mLock) {
- // Find Views collect the translation data
- final ArrayList<ViewTranslationRequest> requests = new ArrayList<>();
- final ArrayList<View> foundViews = new ArrayList<>();
- findViewsTraversalByAutofillIds(views, foundViews);
- for (int i = 0; i < foundViews.size(); i++) {
- final View view = foundViews.get(i);
- final int currentCount = i;
- mActivity.runOnUiThread(() -> {
- final ViewTranslationRequest request = view.onCreateTranslationRequest();
- if (request != null
- && request.getKeys().size() > 0) {
- requests.add(request);
+ // Filter the request views's AutofillId
+ SparseIntArray virtualViewChildCount = getRequestVirtualViewChildCount(views);
+ Map<AutofillId, long[]> viewIds = new ArrayMap<>();
+ for (int i = 0; i < views.size(); i++) {
+ AutofillId autofillId = views.get(i);
+ if (autofillId.isNonVirtual()) {
+ viewIds.put(autofillId, null);
+ } else {
+ // The virtual id get from content capture is long, see getVirtualChildLongId()
+ // e.g. 1001, 1001:2, 1002:1 -> 1001, <1,2>; 1002, <1>
+ AutofillId virtualViewAutofillId = new AutofillId(autofillId.getViewId());
+ long[] childs;
+ if (viewIds.containsKey(virtualViewAutofillId)) {
+ childs = viewIds.get(virtualViewAutofillId);
+ } else {
+ int childCount = virtualViewChildCount.get(autofillId.getViewId());
+ childs = new long[childCount];
+ viewIds.put(virtualViewAutofillId, childs);
}
- if (currentCount == (foundViews.size() - 1)) {
- Log.v(TAG, "onUiTranslationStarted: collect " + requests.size()
- + " requests.");
- mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
- UiTranslationController::sendTranslationRequest,
- UiTranslationController.this, translator, requests));
- }
- });
+ int end = childs.length - 1;
+ childs[end] = autofillId.getVirtualChildLongId();
+ }
}
+ ArrayList<ViewTranslationRequest> requests = new ArrayList<>();
+ int[] supportedFormats = getSupportedFormatsLocked();
+ ArrayList<ViewRootImpl> roots =
+ WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+ mActivity.runOnUiThread(() -> {
+ // traverse the hierarchy to collect ViewTranslationRequests
+ for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+ View rootView = roots.get(rootNum).getView();
+ // TODO(b/183589662): call getTranslationCapabilities() for capability
+ rootView.dispatchRequestTranslation(viewIds, supportedFormats, /* capability */
+ null, requests);
+ }
+ mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
+ UiTranslationController::sendTranslationRequest,
+ UiTranslationController.this, translator, requests));
+ });
}
}
- private void findViewsTraversalByAutofillIds(List<AutofillId> sourceViewIds,
- ArrayList<View> foundViews) {
+ private SparseIntArray getRequestVirtualViewChildCount(List<AutofillId> views) {
+ SparseIntArray virtualViewCount = new SparseIntArray();
+ for (int i = 0; i < views.size(); i++) {
+ AutofillId autofillId = views.get(i);
+ if (!autofillId.isNonVirtual()) {
+ int virtualViewId = autofillId.getViewId();
+ if (virtualViewCount.indexOfKey(virtualViewId) < 0) {
+ virtualViewCount.put(virtualViewId, 1);
+ } else {
+ virtualViewCount.put(virtualViewId, (virtualViewCount.get(virtualViewId) + 1));
+ }
+ }
+ }
+ return virtualViewCount;
+ }
+
+ private int[] getSupportedFormatsLocked() {
+ // We only support text now
+ return new int[] {TranslationSpec.DATA_FORMAT_TEXT};
+ }
+
+ private void findViewsTraversalByAutofillIds(List<Integer> sourceViewIds) {
final ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
final View rootView = roots.get(rootNum).getView();
if (rootView instanceof ViewGroup) {
- findViewsTraversalByAutofillIds((ViewGroup) rootView, sourceViewIds, foundViews);
+ findViewsTraversalByAutofillIds((ViewGroup) rootView, sourceViewIds);
} else {
- addViewIfNeeded(sourceViewIds, rootView, foundViews);
+ addViewIfNeeded(sourceViewIds, rootView);
}
}
}
private void findViewsTraversalByAutofillIds(ViewGroup viewGroup,
- List<AutofillId> sourceViewIds, ArrayList<View> foundViews) {
+ List<Integer> sourceViewIds) {
final int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; ++i) {
final View child = viewGroup.getChildAt(i);
if (child instanceof ViewGroup) {
- findViewsTraversalByAutofillIds((ViewGroup) child, sourceViewIds, foundViews);
+ findViewsTraversalByAutofillIds((ViewGroup) child, sourceViewIds);
} else {
- addViewIfNeeded(sourceViewIds, child, foundViews);
+ addViewIfNeeded(sourceViewIds, child);
}
}
}
- private void addViewIfNeeded(List<AutofillId> sourceViewIds, View view,
- ArrayList<View> foundViews) {
+ private void addViewIfNeeded(List<Integer> sourceViewIds, View view) {
final AutofillId autofillId = view.getAutofillId();
- if (sourceViewIds.contains(autofillId)) {
+ if (sourceViewIds.contains(autofillId.getViewId()) && !mViews.containsKey(autofillId)) {
mViews.put(autofillId, new WeakReference<>(view));
- foundViews.add(view);
}
}
- private void runForEachView(Consumer<View> action) {
+ private void runForEachView(BiConsumer<View, ViewTranslationCallback> action) {
synchronized (mLock) {
final ArrayMap<AutofillId, WeakReference<View>> views = new ArrayMap<>(mViews);
mActivity.runOnUiThread(() -> {
final int viewCounts = views.size();
for (int i = 0; i < viewCounts; i++) {
final View view = views.valueAt(i).get();
- if (view == null) {
+ if (view == null || view.getViewTranslationCallback() == null) {
if (DEBUG) {
- Log.d(TAG, "View was gone for autofillid = " + views.keyAt(i));
+ Log.d(TAG, "View was gone or ViewTranslationCallback for autofillid "
+ + "= " + views.keyAt(i));
}
continue;
}
- action.accept(view);
+ action.accept(view, view.getViewTranslationCallback());
}
});
}
diff --git a/core/java/android/view/translation/ViewTranslationCallback.java b/core/java/android/view/translation/ViewTranslationCallback.java
new file mode 100644
index 0000000..c895984
--- /dev/null
+++ b/core/java/android/view/translation/ViewTranslationCallback.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.translation;
+
+import android.annotation.NonNull;
+import android.annotation.UiThread;
+import android.view.View;
+
+/**
+ * Callback for handling the translated information show or hide in the {@link View}. See {@link
+ * View#onTranslationResponse} for how to get the translated information.
+ */
+@UiThread
+public interface ViewTranslationCallback {
+ /**
+ * Called when the translated text is ready to show or if the user has requested to reshow the
+ * translated content after hiding it. This may be called before the translation results are
+ * returned through the {@link View#onTranslationResponse}.
+ *
+ * @return {@code true} if the View handles showing the translation.
+ */
+ boolean onShowTranslation(@NonNull View view);
+ /**
+ * Called when the user wants to show the original text instead of the translated text. This
+ * may be called before the translation results are returned through the
+ * {@link View#onTranslationResponse}.
+ *
+ * @return {@code true} if the View handles hiding the translation.
+ */
+ boolean onHideTranslation(@NonNull View view);
+ /**
+ * Called when the user finish the Ui translation and no longer to show the translated text.
+ *
+ * @return {@code true} if the View handles clearing the translation.
+ */
+ boolean onClearTranslation(@NonNull View view);
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 487d13e..6d4fa65 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -45,6 +45,7 @@
import android.print.PrintDocumentAdapter;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.LongSparseArray;
import android.util.SparseArray;
import android.view.DragEvent;
import android.view.KeyEvent;
@@ -64,6 +65,9 @@
import android.view.inputmethod.InputConnection;
import android.view.inspector.InspectableProperty;
import android.view.textclassifier.TextClassifier;
+import android.view.translation.TranslationSpec.DataFormat;
+import android.view.translation.ViewTranslationRequest;
+import android.view.translation.ViewTranslationResponse;
import android.widget.AbsoluteLayout;
import java.io.BufferedWriter;
@@ -73,6 +77,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* A View that displays web pages.
@@ -2854,6 +2859,20 @@
return mProvider.getViewDelegate().isVisibleToUserForAutofill(virtualId);
}
+ @Override
+ @Nullable
+ public void onCreateTranslationRequests(@NonNull long[] virtualChildIds,
+ @NonNull @DataFormat int[] supportedFormats,
+ @NonNull Consumer<ViewTranslationRequest> requestsCollector) {
+ mProvider.getViewDelegate().onCreateTranslationRequests(virtualChildIds, supportedFormats,
+ requestsCollector);
+ }
+
+ @Override
+ public void onTranslationResponse(@NonNull LongSparseArray<ViewTranslationResponse> response) {
+ mProvider.getViewDelegate().onTranslationResponse(response);
+ }
+
/** @hide */
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 18a110b..2647360 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Intent;
import android.content.res.Configuration;
@@ -33,6 +34,7 @@
import android.os.Handler;
import android.os.Message;
import android.print.PrintDocumentAdapter;
+import android.util.LongSparseArray;
import android.util.SparseArray;
import android.view.DragEvent;
import android.view.KeyEvent;
@@ -47,6 +49,9 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.textclassifier.TextClassifier;
+import android.view.translation.TranslationSpec.DataFormat;
+import android.view.translation.ViewTranslationRequest;
+import android.view.translation.ViewTranslationResponse;
import android.webkit.WebView.HitTestResult;
import android.webkit.WebView.PictureListener;
import android.webkit.WebView.VisualStateCallback;
@@ -56,6 +61,7 @@
import java.io.File;
import java.util.Map;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* WebView backend provider interface: this interface is the abstract backend to a WebView
@@ -358,6 +364,20 @@
@SuppressWarnings("unused") int flags) {
}
+ @SuppressLint("NullableCollection")
+ @Nullable
+ default void onCreateTranslationRequests(
+ @NonNull @SuppressWarnings("unused") long[] virtualChildIds,
+ @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats,
+ @NonNull @SuppressWarnings("unused")
+ Consumer<ViewTranslationRequest> requestsCollector) {
+ }
+
+ default void onTranslationResponse(
+ @NonNull @SuppressWarnings("unused")
+ LongSparseArray<ViewTranslationResponse> response) {
+ }
+
public AccessibilityNodeProvider getAccessibilityNodeProvider();
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 238ce85..48e25c51 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2894,8 +2894,8 @@
final int originalLength = mTextView.getText().length();
Selection.setSelection((Spannable) mTextView.getText(), offset);
final ClipData clip = event.getClipData();
- final ContentInfo payload =
- new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP)
+ final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP)
+ .setDragAndDropPermissions(permissions)
.build();
mTextView.performReceiveContent(payload);
if (dragDropIntoItself) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index cb2bba1..29c78b5 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -6342,6 +6342,9 @@
/**
* Set the ID of the top-level view of the XML layout.
*
+ * The view's ID is changed right after inflation, before it gets added to its parent. The ID
+ * of a given view can never change after the initial inflation.
+ *
* Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
*
* @throws UnsupportedOperationException if the method is called on a RemoteViews defined in
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index a63305e..287c182 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -20,12 +20,10 @@
import android.text.Editable;
import android.text.Selection;
import android.text.Spanned;
-import android.text.TextUtils;
import android.text.method.WordIterator;
import android.text.style.SpellCheckSpan;
import android.text.style.SuggestionSpan;
import android.util.Log;
-import android.util.LruCache;
import android.util.Range;
import android.view.textservice.SentenceSuggestionsInfo;
import android.view.textservice.SpellCheckerSession;
@@ -98,10 +96,6 @@
private Runnable mSpellRunnable;
- private static final int SUGGESTION_SPAN_CACHE_SIZE = 10;
- private final LruCache<Long, SuggestionSpan> mSuggestionSpanCache =
- new LruCache<Long, SuggestionSpan>(SUGGESTION_SPAN_CACHE_SIZE);
-
public SpellChecker(TextView textView) {
mTextView = textView;
@@ -144,7 +138,6 @@
// Remove existing misspelled SuggestionSpans
mTextView.removeMisspelledSpans((Editable) mTextView.getText());
- mSuggestionSpanCache.evictAll();
}
private void setLocale(Locale locale) {
@@ -410,16 +403,7 @@
}
if (spellCheckSpanStart >= 0 && spellCheckSpanEnd > spellCheckSpanStart
&& end > start) {
- final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end));
- final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key);
- if (tempSuggestionSpan != null) {
- if (DBG) {
- Log.i(TAG, "Remove existing misspelled span. "
- + editable.subSequence(start, end));
- }
- editable.removeSpan(tempSuggestionSpan);
- mSuggestionSpanCache.remove(key);
- }
+ removeErrorSuggestionSpan(editable, start, end, RemoveReason.OBSOLETE);
}
}
return spellCheckSpan;
@@ -428,6 +412,35 @@
return null;
}
+ private enum RemoveReason {
+ /**
+ * Indicates the previous SuggestionSpan is replaced by a new SuggestionSpan.
+ */
+ REPLACE,
+ /**
+ * Indicates the previous SuggestionSpan is removed because corresponding text is
+ * considered as valid words now.
+ */
+ OBSOLETE,
+ }
+
+ private static void removeErrorSuggestionSpan(
+ Editable editable, int start, int end, RemoveReason reason) {
+ SuggestionSpan[] spans = editable.getSpans(start, end, SuggestionSpan.class);
+ for (SuggestionSpan span : spans) {
+ if (editable.getSpanStart(span) == start
+ && editable.getSpanEnd(span) == end
+ && (span.getFlags() & (SuggestionSpan.FLAG_MISSPELLED
+ | SuggestionSpan.FLAG_GRAMMAR_ERROR)) != 0) {
+ if (DBG) {
+ Log.i(TAG, "Remove existing misspelled/grammar error span on "
+ + editable.subSequence(start, end) + ", reason: " + reason);
+ }
+ editable.removeSpan(span);
+ }
+ }
+ }
+
@Override
public void onGetSuggestions(SuggestionsInfo[] results) {
final Editable editable = (Editable) mTextView.getText();
@@ -543,16 +556,7 @@
}
SuggestionSpan suggestionSpan =
new SuggestionSpan(mTextView.getContext(), suggestions, flags);
- final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end));
- final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key);
- if (tempSuggestionSpan != null) {
- if (DBG) {
- Log.i(TAG, "Cached span on the same position is cleard. "
- + editable.subSequence(start, end));
- }
- editable.removeSpan(tempSuggestionSpan);
- }
- mSuggestionSpanCache.put(key, suggestionSpan);
+ removeErrorSuggestionSpan(editable, start, end, RemoveReason.REPLACE);
editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTextView.invalidateRegion(start, end, false /* No cursor involved */);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 940a3c9..ffaa315 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -195,7 +195,9 @@
import android.view.textservice.SpellCheckerSubtype;
import android.view.textservice.TextServicesManager;
import android.view.translation.TranslationRequestValue;
+import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationController;
+import android.view.translation.ViewTranslationCallback;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
import android.widget.RemoteViews.RemoteView;
@@ -203,6 +205,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastMath;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.EditableInputConnection;
@@ -737,7 +740,7 @@
private MovementMethod mMovement;
private TransformationMethod mTransformation;
- private TranslationTransformationMethod mTranslationTransformation;
+ private TextViewTranslationCallback mDefaultTranslationCallback;
@UnsupportedAppUsage
private boolean mAllowTransformationLengthChange;
@UnsupportedAppUsage
@@ -13857,136 +13860,104 @@
}
/**
- * Provides a {@link ViewTranslationRequest} that represents the content to be translated via
- * translation service.
+ * Returns a {@link ViewTranslationRequest} which represents the content to be translated.
*
- * <p>NOTE: When overriding the method, it should not translate the password. We also suggest
- * that not translating the text is selectable or editable. We use the transformation method to
- * implement showing the translated text. The TextView does not support the transformation
- * method text length change. If the text is selectable or editable, it will crash while
- * selecting the text. To support it, it needs broader changes to text APIs, we only allow to
- * translate non selectable and editable text now.
+ * <p>NOTE: When overriding the method, it should not translate the password. If the subclass
+ * uses {@link TransformationMethod} to display the translated result, it's also not recommend
+ * to translate text is selectable or editable.
*
- * @hide
+ * @param supportedFormats the supported translation format. The value could be {@link
+ * android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}.
+ * @return the {@link ViewTranslationRequest} which contains the information to be translated.
*/
@Nullable
@Override
- public ViewTranslationRequest onCreateTranslationRequest() {
- if (mText == null || mText.length() == 0) {
+ public ViewTranslationRequest onCreateTranslationRequest(@NonNull int[] supportedFormats) {
+ if (supportedFormats == null || supportedFormats.length == 0) {
// TODO(b/182433547): remove before S release
if (UiTranslationController.DEBUG) {
- Log.w(LOG_TAG, "Cannot create translation request for the empty text.");
+ Log.w(LOG_TAG, "Do not provide the support translation formats.");
}
return null;
}
- // Not translate password, editable text and not important for translation
- // TODO(b/177214256): support selectable text translation. It needs to broader changes to
- // text selection apis, not support in S.
- boolean isPassword = isAnyPasswordInputType() || hasPasswordTransformationMethod();
- if (isTextEditable() || isPassword || isTextSelectable()) {
- // TODO(b/182433547): remove before S release
- if (UiTranslationController.DEBUG) {
- Log.w(LOG_TAG, "Cannot create translation request. editable = " + isTextEditable()
- + ", isPassword = " + isPassword + ", selectable = " + isTextSelectable());
+ ViewTranslationRequest.Builder requestBuilder =
+ new ViewTranslationRequest.Builder(getAutofillId());
+ // Support Text translation
+ if (ArrayUtils.contains(supportedFormats, TranslationSpec.DATA_FORMAT_TEXT)) {
+ if (mText == null || mText.length() == 0) {
+ // TODO(b/182433547): remove before S release
+ if (UiTranslationController.DEBUG) {
+ Log.w(LOG_TAG, "Cannot create translation request for the empty text.");
+ }
+ return null;
}
- return null;
+ boolean isPassword = isAnyPasswordInputType() || hasPasswordTransformationMethod();
+ // TODO(b/177214256): support selectable text translation.
+ // We use the TransformationMethod to implement showing the translated text. The
+ // TextView does not support the text length change for TransformationMethod. If the
+ // text is selectable or editable, it will crash while selecting the text. To support
+ // it, it needs broader changes to text APIs, we only allow to translate non selectable
+ // and editable text in S.
+ if (isTextEditable() || isPassword || isTextSelectable()) {
+ // TODO(b/182433547): remove before S release
+ if (UiTranslationController.DEBUG) {
+ Log.w(LOG_TAG, "Cannot create translation request. editable = "
+ + isTextEditable() + ", isPassword = " + isPassword + ", selectable = "
+ + isTextSelectable());
+ }
+ return null;
+ }
+ // TODO(b/176488462): apply the view's important for translation
+ requestBuilder.setValue(ViewTranslationRequest.ID_TEXT,
+ TranslationRequestValue.forText(mText));
}
- // TODO(b/176488462): apply the view's important for translation property
- // TODO(b/174283799): remove the spans from the mText and save the spans information
- // TODO: use fixed ids for request texts.
- ViewTranslationRequest request =
- new ViewTranslationRequest.Builder(getAutofillId())
- .setValue(ViewTranslationRequest.ID_TEXT,
- TranslationRequestValue.forText(mText))
- .build();
- return request;
+ return requestBuilder.build();
}
/**
- * Provides the implementation that pauses the ongoing Ui translation, it will show the original
- * text instead of the translated text and restore the original transformation method.
+ * Returns a {@link ViewTranslationCallback} that is used to display the translated information.
+ * The default implementation will use a {@link TransformationMethod} that allow to replace the
+ * current {@link TransformationMethod} to transform the original text to the translated text
+ * display.
*
- * <p>NOTE: If this method is overridden, other translation related methods such as
- * {@link onRestoreUiTranslation}, {@link onFinishUiTranslation}, {@link onTranslationComplete}
- * should also be overridden.
- *
- * @hide
+ * @return a {@link ViewTranslationCallback} that is used to control how to display the
+ * translated information or {@code null} if this View doesn't support translation.
*/
+ @Nullable
@Override
- public void onPauseUiTranslation() {
- // Restore to original text content.
- if (mTranslationTransformation != null) {
- setTransformationMethod(mTranslationTransformation.getOriginalTransformationMethod());
- } else {
- // TODO(b/182433547): remove before S release
- Log.w(LOG_TAG, "onPauseUiTranslation(): no translated text.");
+ public ViewTranslationCallback getViewTranslationCallback() {
+ return getDefaultViewTranslationCallback();
+ }
+
+ private ViewTranslationCallback getDefaultViewTranslationCallback() {
+ if (mDefaultTranslationCallback == null) {
+ mDefaultTranslationCallback = new TextViewTranslationCallback();
}
+ return mDefaultTranslationCallback;
}
/**
- * Provides the implementation that restoes the paused Ui translation, it will show the
- * translated text again if the text had been translated. This method will replace the current
- * tansformation method with {@link TranslationTransformationMethod}.
*
- * <p>NOTE: If this method is overridden, other translation related methods such as
- * {@link onPauseUiTranslation}, {@link onFinishUiTranslation}, {@link onTranslationComplete}
- * should also be overridden.
+ * Called when the content from {@link #onCreateTranslationRequest} had been translated by the
+ * TranslationService. The default implementation will replace the current
+ * {@link TransformationMethod} to transform the original text to the translated text display.
*
- * @hide
+ * @param response a {@link ViewTranslationResponse} that contains the translated information
+ * which can be shown in the view.
*/
@Override
- public void onRestoreUiTranslation() {
- if (mTranslationTransformation != null) {
- setTransformationMethod(mTranslationTransformation);
- } else {
- // TODO(b/182433547): remove before S release
- Log.w(LOG_TAG, "onRestoreUiTranslation(): no translated text.");
- }
- }
-
- /**
- * Provides the implementation that finishes the current Ui translation and it's no longer to
- * show the translated text. This method restores the original transformation method and resets
- * the saved {@link TranslationTransformationMethod}.
- *
- * <p>NOTE: If this method is overridden, other translation related methods such as
- * {@link onPauseUiTranslation}, {@link onRestoreUiTranslation}, {@link onTranslationComplete}
- * should also be overridden.
- *
- * @hide
- */
- @Override
- public void onFinishUiTranslation() {
- // Restore to original text content and clear TranslationTransformation
- if (mTranslationTransformation != null) {
- setTransformationMethod(mTranslationTransformation.getOriginalTransformationMethod());
- mTranslationTransformation = null;
- } else {
- // TODO(b/182433547): remove before S release
- Log.w(LOG_TAG, "onFinishUiTranslation(): no translated text.");
- }
- }
-
- /**
- * Default {@link TextView} implementation after the translation request is done by the
- * translation service, it's ok to show the translated text. This method will save the original
- * transformation method and replace the current transformation method with
- * {@link TranslationTransformationMethod}.
- *
- * <p>NOTE: If this method is overridden, other translation related methods such as
- * {@link onPauseUiTranslation}, {@link onRestoreUiTranslation}, {@link onFinishUiTranslation}
- * should also be overridden.
- *
- * @hide
- */
- @Override
- public void onTranslationComplete(@NonNull ViewTranslationResponse response) {
- // Show the translated text.
- TransformationMethod originalTranslationMethod = mTranslationTransformation != null
- ? mTranslationTransformation.getOriginalTransformationMethod() : mTransformation;
- mTranslationTransformation =
+ public void onTranslationResponse(@NonNull ViewTranslationResponse response) {
+ // TODO(b/183467275): Use the overridden ViewTranslationCallback instead of our default
+ // implementation if the view has overridden getViewTranslationCallback.
+ TextViewTranslationCallback callback =
+ (TextViewTranslationCallback) getDefaultViewTranslationCallback();
+ TranslationTransformationMethod oldTranslationMethod =
+ callback.getTranslationTransformation();
+ TransformationMethod originalTranslationMethod = oldTranslationMethod != null
+ ? oldTranslationMethod.getOriginalTransformationMethod() : mTransformation;
+ TranslationTransformationMethod newTranslationMethod =
new TranslationTransformationMethod(response, originalTranslationMethod);
// TODO(b/178353965): well-handle setTransformationMethod.
- setTransformationMethod(mTranslationTransformation);
+ callback.setTranslationTransformation(newTranslationMethod);
}
}
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
new file mode 100644
index 0000000..296d93c
--- /dev/null
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.annotation.NonNull;
+import android.text.method.TranslationTransformationMethod;
+import android.util.Log;
+import android.view.View;
+import android.view.translation.UiTranslationManager;
+import android.view.translation.ViewTranslationCallback;
+import android.view.translation.ViewTranslationResponse;
+
+/**
+ * Default implementation for {@link ViewTranslationCallback} for {@link TextView} components.
+ * This class handles how to display the translated information for {@link TextView}.
+ *
+ * @hide
+ */
+public class TextViewTranslationCallback implements ViewTranslationCallback {
+
+ private static final String TAG = "TextViewTranslationCallback";
+
+ private static final boolean DEBUG = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
+
+ private TranslationTransformationMethod mTranslationTransformation;
+
+ /**
+ * Invoked by the platform when receiving the successful {@link ViewTranslationResponse} for the
+ * view that provides the translatable information by {@link View#createTranslationRequest} and
+ * sent by the platform.
+ */
+ void setTranslationTransformation(TranslationTransformationMethod method) {
+ if (method == null) {
+ if (DEBUG) {
+ Log.w(TAG, "setTranslationTransformation: should not set null "
+ + "TranslationTransformationMethod");
+ }
+ return;
+ }
+ mTranslationTransformation = method;
+ }
+
+ TranslationTransformationMethod getTranslationTransformation() {
+ return mTranslationTransformation;
+ }
+
+ private void clearTranslationTransformation() {
+ if (DEBUG) {
+ Log.v(TAG, "clearTranslationTransformation: " + mTranslationTransformation);
+ }
+ mTranslationTransformation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onShowTranslation(@NonNull View view) {
+ if (mTranslationTransformation != null) {
+ ((TextView) view).setTransformationMethod(mTranslationTransformation);
+ } else {
+ if (DEBUG) {
+ // TODO(b/182433547): remove before S release
+ Log.w(TAG, "onShowTranslation(): no translated text.");
+ }
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onHideTranslation(@NonNull View view) {
+ // Restore to original text content.
+ if (mTranslationTransformation != null) {
+ ((TextView) view).setTransformationMethod(
+ mTranslationTransformation.getOriginalTransformationMethod());
+ } else {
+ if (DEBUG) {
+ // TODO(b/182433547): remove before S release
+ Log.w(TAG, "onHideTranslation(): no translated text.");
+ }
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onClearTranslation(@NonNull View view) {
+ // Restore to original text content and clear TranslationTransformation
+ if (mTranslationTransformation != null) {
+ ((TextView) view).setTransformationMethod(
+ mTranslationTransformation.getOriginalTransformationMethod());
+ clearTranslationTransformation();
+ } else {
+ if (DEBUG) {
+ // TODO(b/182433547): remove before S release
+ Log.w(TAG, "onClearTranslation(): no translated text.");
+ }
+ }
+ return true;
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 7baa53b..a600a94 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -133,6 +133,17 @@
duration);
}
+ /**
+ * Logs the activated mode of the magnification when the IME window is shown on the screen.
+ * Calls this when the magnification is enabled and the IME window is shown on the screen.
+ *
+ * @param mode The activated magnification mode.
+ */
+ public static void logMagnificationModeWithImeOn(int mode) {
+ FrameworkStatsLog.write(FrameworkStatsLog.MAGNIFICATION_MODE_WITH_IME_ON_REPORTED,
+ convertToLoggingMagnificationMode(mode));
+ }
+
private static int convertToLoggingShortcutType(@ShortcutType int shortcutType) {
switch (shortcutType) {
case ACCESSIBILITY_BUTTON:
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 922f96e..3b6a877 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -221,32 +221,19 @@
@Override
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- if (mIsSendAction) {
- showEmptyState(listAdapter,
- R.drawable.ic_no_apps,
- R.string.resolver_no_personal_apps_available_share,
- /* subtitleRes */ 0);
- } else {
- showEmptyState(listAdapter,
- R.drawable.ic_no_apps,
- R.string.resolver_no_personal_apps_available_resolve,
- /* subtitleRes */ 0);
- }
+ showEmptyState(listAdapter,
+ R.drawable.ic_no_apps,
+ R.string.resolver_no_personal_apps_available,
+ /* subtitleRes */ 0);
+
}
@Override
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
- if (mIsSendAction) {
- showEmptyState(listAdapter,
- R.drawable.ic_no_apps,
- R.string.resolver_no_work_apps_available_share,
- /* subtitleRes */ 0);
- } else {
- showEmptyState(listAdapter,
- R.drawable.ic_no_apps,
- R.string.resolver_no_work_apps_available_resolve,
- /* subtitleRes */ 0);
- }
+ showEmptyState(listAdapter,
+ R.drawable.ic_no_apps,
+ R.string.resolver_no_work_apps_available,
+ /* subtitleRes */ 0);
}
void setEmptyStateBottomOffset(int bottomOffset) {
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index eecd0cf..01bb199 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -20,6 +20,7 @@
import android.app.AsyncNotedAppOp;
import android.app.SyncNotedAppOp;
import android.app.RuntimeAppOpAccessMessage;
+import android.content.AttributionSource;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
import android.os.RemoteCallback;
@@ -52,17 +53,13 @@
// End of methods also called by native code.
// Any new method exposed to native must be added after the last one, do not reorder
- int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
- String proxiedAttributionTag, int proxyUid, String proxyPackageName,
- String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage);
- int startProxyOperation(IBinder clientId, int code, int proxiedUid, String proxiedPackageName,
- @nullable String proxiedAttributionTag, int proxyUid, String proxyPackageName,
- @nullable String proxyAttributionTag, boolean startIfModeDefault,
- boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
- void finishProxyOperation(IBinder clientId, int code, int proxiedUid, String proxiedPackageName,
- @nullable String proxiedAttributionTag, int proxyUid, String proxyPackageName,
- @nullable String proxyAttributionTag);
+ int noteProxyOperation(int code, in AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation);
+ int startProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource,
+ boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
+ boolean shouldCollectMessage, boolean skipProxyOperation);
+ void finishProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource);
// Remaining methods are only used in Java.
int checkPackage(int uid, String packageName);
@@ -83,6 +80,7 @@
void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep);
void addHistoricalOps(in AppOpsManager.HistoricalOps ops);
void resetHistoryParameters();
+ void resetPackageOpsNoHistory(String packageName);
void clearHistory();
void rebootHistory(long offlineDurationMillis);
List<AppOpsManager.PackageOps> getUidOps(int uid, in int[] ops);
@@ -100,6 +98,8 @@
void startWatchingActive(in int[] ops, IAppOpsActiveCallback callback);
void stopWatchingActive(IAppOpsActiveCallback callback);
boolean isOperationActive(int code, int uid, String packageName);
+ boolean isProxying(int op, String proxyPackageName, String proxyAttributionTag, int proxiedUid,
+ String proxiedPackageName);
void startWatchingStarted(in int[] ops, IAppOpsStartedCallback callback);
void stopWatchingStarted(IAppOpsStartedCallback callback);
diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
index 23314e7..9bec505 100644
--- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
+++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
@@ -66,4 +66,12 @@
* Called when the recognition is resumed after it was temporarily paused.
*/
void onRecognitionResumed();
+
+ /**
+ * Called when the {@link HotwordDetectionService} reported the result for requesting update
+ * state action.
+ *
+ * @param status The status about the result of requesting update state action.
+ */
+ void onStatusReported(int status);
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 2a022e6..fffeb02 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -18,12 +18,15 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.media.AudioFormat;
import android.media.permission.Identity;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.SharedMemory;
+import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
@@ -33,6 +36,7 @@
import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
interface IVoiceInteractionManagerService {
void showSession(in Bundle sessionArgs, int flags);
@@ -229,18 +233,33 @@
/**
* Set configuration and pass read-only data to hotword detection service.
*
- * @param options Application configuration data provided by the
- * {@link VoiceInteractionService}. PersistableBundle does not allow any remotable objects or
+ * @param options Application configuration data to provide to the
+ * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
* other contents that can be used to communicate with other processes.
- * @param sharedMemory The unrestricted data blob provided by the
- * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+ * @param sharedMemory The unrestricted data blob to provide to the
+ * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
* such data to the trusted process.
+ * @param callback Use this to report {@link HotwordDetectionService} status.
*/
- void setHotwordDetectionServiceConfig(
- in PersistableBundle options, in SharedMemory sharedMemory);
+ void updateState(
+ in PersistableBundle options,
+ in SharedMemory sharedMemory,
+ in IHotwordRecognitionStatusCallback callback);
/**
* Requests to shutdown hotword detection service.
*/
void shutdownHotwordDetectionService();
+
+ void startListeningFromMic(
+ in AudioFormat audioFormat,
+ in IMicrophoneHotwordDetectionVoiceInteractionCallback callback);
+
+ void stopListeningFromMic();
+
+ void startListeningFromExternalSource(
+ in ParcelFileDescriptor audioStream,
+ in AudioFormat audioFormat,
+ in PersistableBundle options,
+ in IMicrophoneHotwordDetectionVoiceInteractionCallback callback);
}
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index a2f014c..622f166 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -221,7 +221,7 @@
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
- R.string.resolver_no_personal_apps_available_resolve,
+ R.string.resolver_no_personal_apps_available,
/* subtitleRes */ 0);
}
@@ -229,7 +229,7 @@
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
- R.string.resolver_no_work_apps_available_resolve,
+ R.string.resolver_no_work_apps_available,
/* subtitleRes */ 0);
}
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 762297d..86f29a8 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -106,13 +106,17 @@
}
private String resolveTitle() {
- final int titleId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getTitleResId()
- : ID_NULL;
- if (titleId != ID_NULL && mSuspendingAppResources != null) {
- try {
- return mSuspendingAppResources.getString(titleId);
- } catch (Resources.NotFoundException nfe) {
- Slog.e(TAG, "Could not resolve string resource id " + titleId);
+ if (mSuppliedDialogInfo != null) {
+ final int titleId = mSuppliedDialogInfo.getTitleResId();
+ final String title = mSuppliedDialogInfo.getTitle();
+ if (titleId != ID_NULL && mSuspendingAppResources != null) {
+ try {
+ return mSuspendingAppResources.getString(titleId);
+ } catch (Resources.NotFoundException nfe) {
+ Slog.e(TAG, "Could not resolve string resource id " + titleId);
+ }
+ } else if (title != null) {
+ return title;
}
}
return getString(R.string.app_suspended_title);
@@ -159,13 +163,17 @@
Slog.w(TAG, "Unknown neutral button action: " + mNeutralButtonAction);
return null;
}
- final int buttonTextId = (mSuppliedDialogInfo != null)
- ? mSuppliedDialogInfo.getNeutralButtonTextResId() : ID_NULL;
- if (buttonTextId != ID_NULL && mSuspendingAppResources != null) {
- try {
- return mSuspendingAppResources.getString(buttonTextId);
- } catch (Resources.NotFoundException nfe) {
- Slog.e(TAG, "Could not resolve string resource id " + buttonTextId);
+ if (mSuppliedDialogInfo != null) {
+ final int buttonTextId = mSuppliedDialogInfo.getNeutralButtonTextResId();
+ final String buttonText = mSuppliedDialogInfo.getNeutralButtonText();
+ if (buttonTextId != ID_NULL && mSuspendingAppResources != null) {
+ try {
+ return mSuspendingAppResources.getString(buttonTextId);
+ } catch (Resources.NotFoundException nfe) {
+ Slog.e(TAG, "Could not resolve string resource id " + buttonTextId);
+ }
+ } else if (buttonText != null) {
+ return buttonText;
}
}
return getString(defaultButtonTextId);
diff --git a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
index 1c222a7..9a02b7b 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
@@ -40,8 +40,7 @@
overrides = new HashMap<>();
for (int i = 0; i < keyCount; i++) {
long key = in.readLong();
- PackageOverride override = in.readParcelable(PackageOverride.class.getClassLoader());
- overrides.put(key, override);
+ overrides.put(key, PackageOverride.createFromParcel(in));
}
}
@@ -55,7 +54,7 @@
dest.writeInt(overrides.size());
for (Long key : overrides.keySet()) {
dest.writeLong(key);
- dest.writeParcelable(overrides.get(key), 0);
+ overrides.get(key).writeToParcel(dest);
}
}
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 249c134..afcd0b0 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -151,15 +151,23 @@
void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
/**
- * Adds overrides to compatibility changes.
+ * Adds overrides to compatibility changes on release builds.
*
- * <p>Kills the app to allow the changes to take effect.
+ * <p>The caller to this API needs to hold
+ * {@code android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD} and all change ids
+ * in {@code overrides} need to annotated with {@link android.compat.annotation.Overridable}.
+ *
+ * A release build in this definition means that {@link android.os.Build#IS_DEBUGGABLE} needs to
+ * be {@code false}.
+ *
+ * <p>Note that this does not kill the app, and therefore overrides read from the app process
+ * will not be updated. Overrides read from the system process do take effect.
*
* @param overrides parcelable containing the compat change overrides to be applied
* @param packageName the package name of the app whose changes will be overridden
* @throws SecurityException if overriding changes is not permitted
*/
- void setOverridesFromInstaller(in CompatibilityOverrideConfig overrides, in String packageName);
+ void setOverridesOnReleaseBuilds(in CompatibilityOverrideConfig overrides, in String packageName);
/**
* Adds overrides to compatibility changes.
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index fae5862..6776c27 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -20,6 +20,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -28,6 +29,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
+import android.view.Display;
import java.util.LinkedList;
import java.util.Queue;
@@ -52,6 +54,7 @@
// This value is approximately 1/3 of the smallest possible brightness value.
public static final float EPSILON = 0.001f;
+ private DisplayManager mDisplayManager;
private final Context mContext;
private final Queue<Object> mWriteHistory = new LinkedList<>();
@@ -87,11 +90,15 @@
* value, if float is invalid. If both are invalid, use default float value from config.
*/
public void startSynchronizing() {
+ if (mDisplayManager == null) {
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ }
+
final BrightnessSyncObserver brightnessSyncObserver;
brightnessSyncObserver = new BrightnessSyncObserver(mHandler);
brightnessSyncObserver.startObserving();
- final float currentFloatBrightness = getScreenBrightnessFloat(mContext);
+ final float currentFloatBrightness = getScreenBrightnessFloat();
final int currentIntBrightness = getScreenBrightnessInt(mContext);
if (!Float.isNaN(currentFloatBrightness)) {
@@ -101,9 +108,7 @@
} else {
final float defaultBrightness = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_screenBrightnessSettingDefaultFloat);
- Settings.System.putFloatForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_FLOAT, defaultBrightness,
- UserHandle.USER_CURRENT);
+ mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, defaultBrightness);
}
}
@@ -135,7 +140,7 @@
/**
* Translates specified value from the float brightness system to the int brightness system,
* given the min/max of each range. Accounts for special values such as OFF and invalid values.
- * Value returned as a float privimite (to preserve precision), but is a value within the
+ * Value returned as a float primitive (to preserve precision), but is a value within the
* int-system range.
*/
public static float brightnessFloatToIntRange(float brightnessFloat) {
@@ -152,10 +157,8 @@
}
}
- private static float getScreenBrightnessFloat(Context context) {
- return Settings.System.getFloatForUser(context.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_FLOAT, PowerManager.BRIGHTNESS_INVALID_FLOAT,
- UserHandle.USER_CURRENT);
+ private float getScreenBrightnessFloat() {
+ return mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY);
}
private static int getScreenBrightnessInt(Context context) {
@@ -184,9 +187,7 @@
float newBrightnessFloat = brightnessIntToFloat(value);
mWriteHistory.offer(newBrightnessFloat);
mPreferredSettingValue = newBrightnessFloat;
- Settings.System.putFloatForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_FLOAT, newBrightnessFloat,
- UserHandle.USER_CURRENT);
+ mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat);
}
}
@@ -255,7 +256,7 @@
mHandler.removeMessages(MSG_UPDATE_FLOAT);
mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget();
} else if (BRIGHTNESS_FLOAT_URI.equals(uri)) {
- float currentFloat = getScreenBrightnessFloat(mContext);
+ float currentFloat = getScreenBrightnessFloat();
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/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index db14034..75a9813 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -84,16 +84,16 @@
// Subtract what the apps used, but clamp to 0.
final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs);
- final double systemComponentPowerMah = Math.max(0, systemPowerMah - total.powerMah);
- if (DEBUG && systemComponentPowerMah != 0) {
+ if (DEBUG) {
Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs)
- + " power=" + formatCharge(systemComponentPowerMah));
+ + " power=" + formatCharge(systemPowerMah));
}
systemBatteryConsumerBuilder
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH,
systemComponentDurationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
- systemComponentPowerMah);
+ Math.max(systemPowerMah, total.powerMah))
+ .setPowerConsumedByApps(total.powerMah);
}
private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index 498e1f2..f6ef30c 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -42,8 +42,9 @@
private static class PowerAndDuration {
public long durationMs;
- public double powerMah;
+ public double remainingPowerMah;
public long totalAppDurationMs;
+ public double totalAppPowerMah;
public long signalDurationMs;
public long noCoverageDurationMs;
}
@@ -103,12 +104,14 @@
calculateRemaining(total, batteryStats, rawRealtimeUs,
query.shouldForceUsePowerProfileModel());
- if (total.powerMah != 0) {
+ if (total.remainingPowerMah != 0 || total.totalAppPowerMah != 0) {
builder.getOrCreateSystemBatteryConsumerBuilder(
SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO)
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO,
total.durationMs)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, total.powerMah);
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ total.remainingPowerMah + total.totalAppPowerMah)
+ .setPowerConsumedByApps(total.totalAppPowerMah);
}
}
@@ -120,6 +123,7 @@
final double powerMah = calculatePower(u, powerPerPacketMah, radioActiveDurationMs,
shouldForceUsePowerProfileModel);
+ total.totalAppPowerMah += powerMah;
app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO,
radioActiveDurationMs)
@@ -142,14 +146,14 @@
BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
calculateRemaining(total, batteryStats, rawRealtimeUs, false);
- if (total.powerMah != 0) {
+ if (total.remainingPowerMah != 0) {
if (total.signalDurationMs != 0) {
radio.noCoveragePercent =
total.noCoverageDurationMs * 100.0 / total.signalDurationMs;
}
radio.mobileActive = total.durationMs;
radio.mobileActiveCount = batteryStats.getMobileRadioActiveUnknownCount(statsType);
- radio.mobileRadioPowerMah = total.powerMah;
+ radio.mobileRadioPowerMah = total.remainingPowerMah;
radio.sumPower();
}
if (radio.totalPowerMah > 0) {
@@ -265,7 +269,7 @@
}
}
total.durationMs = radioActiveTimeMs;
- total.powerMah = powerMah;
+ total.remainingPowerMah = powerMah;
total.signalDurationMs = signalTimeMs;
}
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index d94bb31..ad57444 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -66,11 +66,7 @@
batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED,
query.shouldForceUsePowerProfileModel());
- builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN)
- .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE,
- totalPowerAndDuration.durationMs)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE,
- totalPowerAndDuration.powerMah);
+ double totalAppPower = 0;
// Now deal with each app's UidBatteryConsumer. The results are stored in the
// BatteryConsumer.POWER_COMPONENT_SCREEN power component, which is considered smeared,
@@ -87,11 +83,20 @@
appPowerAndDuration.durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
appPowerAndDuration.powerMah);
+ totalAppPower += appPowerAndDuration.powerMah;
}
} else {
smearScreenBatteryDrain(uidBatteryConsumerBuilders, totalPowerAndDuration,
rawRealtimeUs);
+ totalAppPower = totalPowerAndDuration.powerMah;
}
+
+ builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN)
+ .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE,
+ totalPowerAndDuration.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE,
+ Math.max(totalPowerAndDuration.powerMah, totalAppPower))
+ .setPowerConsumedByApps(totalAppPower);
}
/**
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index b6bfde7..d95506b 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -121,7 +121,8 @@
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI,
powerDurationAndTraffic.durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
- powerDurationAndTraffic.powerMah);
+ totalAppPowerMah + powerDurationAndTraffic.powerMah)
+ .setPowerConsumedByApps(totalAppPowerMah);
}
/**
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 39bde74..611fe29 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -32,6 +32,7 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -2508,6 +2509,7 @@
if (mDecor.mForceWindowDrawsBarBackgrounds) {
params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
}
+ params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
}
if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
decor.setSystemUiVisibility(
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index fc4cc57..7a8ead6 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.RemotableViewMethod;
@@ -165,11 +166,13 @@
private void updateColors() {
if (shouldShowNumber() && !mDisallowColor) {
mPillView.setBackgroundTintList(ColorStateList.valueOf(mHighlightPillColor));
- mIconView.setColorFilter(mHighlightTextColor);
+ mPillView.setBackgroundTintMode(PorterDuff.Mode.SRC_IN);
+ mIconView.setColorFilter(mHighlightTextColor, PorterDuff.Mode.SRC_IN);
mNumberView.setTextColor(mHighlightTextColor);
} else {
mPillView.setBackgroundTintList(ColorStateList.valueOf(mDefaultPillColor));
- mIconView.setColorFilter(mDefaultTextColor);
+ mPillView.setBackgroundTintMode(PorterDuff.Mode.SRC_IN);
+ mIconView.setColorFilter(mDefaultTextColor, PorterDuff.Mode.SRC_IN);
mNumberView.setTextColor(mDefaultTextColor);
}
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index f1fa5db..20d257e 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -221,7 +221,6 @@
static_libs: [
"libasync_safe",
- "libconnectivityframeworkutils",
"libbinderthreadstateutils",
"libdmabufinfo",
"libgif",
@@ -238,7 +237,6 @@
"android.hardware.camera.device@3.2",
"media_permission-aidl-cpp",
"libandroidicu",
- "libandroid_net",
"libbpf_android",
"libnetdbpf",
"libnetdutils",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 94ac183..0c3f265 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -156,7 +156,6 @@
extern int register_android_service_DataLoaderService(JNIEnv* env);
extern int register_android_os_incremental_IncrementalManager(JNIEnv* env);
extern int register_android_net_LocalSocketImpl(JNIEnv* env);
-extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_Hyphenator(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
@@ -1548,7 +1547,6 @@
REG_JNI(register_android_os_Trace),
REG_JNI(register_android_os_UEventObserver),
REG_JNI(register_android_net_LocalSocketImpl),
- REG_JNI(register_android_net_NetworkUtils),
REG_JNI(register_android_os_MemoryFile),
REG_JNI(register_android_os_SharedMemory),
REG_JNI(register_android_os_incremental_IncrementalManager),
diff --git a/core/jni/android_media_AudioDescriptor.h b/core/jni/android_media_AudioDescriptor.h
new file mode 100644
index 0000000..680ac3f
--- /dev/null
+++ b/core/jni/android_media_AudioDescriptor.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_MEDIA_EXTRAAUDIODESCRIPTOR_H
+#define ANDROID_MEDIA_EXTRAAUDIODESCRIPTOR_H
+
+#include <system/audio.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+// keep these values in sync with ExtraAudioDescriptor.java
+#define STANDARD_NONE 0
+#define STANDARD_EDID 1
+
+static inline status_t audioStandardFromNative(audio_standard_t nStandard, int* standard) {
+ status_t result = NO_ERROR;
+ switch (nStandard) {
+ case AUDIO_STANDARD_NONE:
+ *standard = STANDARD_NONE;
+ break;
+ case AUDIO_STANDARD_EDID:
+ *standard = STANDARD_EDID;
+ break;
+ default:
+ result = BAD_VALUE;
+ }
+ return result;
+}
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_EXTRAAUDIODESCRIPTOR_H
\ No newline at end of file
diff --git a/core/jni/android_media_AudioProfile.h b/core/jni/android_media_AudioProfile.h
new file mode 100644
index 0000000..446bd64
--- /dev/null
+++ b/core/jni/android_media_AudioProfile.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_MEDIA_AUDIOPROFILE_H
+#define ANDROID_MEDIA_AUDIOPROFILE_H
+
+#include <system/audio.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+// keep these values in sync with AudioProfile.java
+#define ENCAPSULATION_TYPE_NONE 0
+#define ENCAPSULATION_TYPE_IEC61937 1
+
+static inline status_t audioEncapsulationTypeFromNative(
+ audio_encapsulation_type_t nEncapsulationType, int* encapsulationType) {
+ status_t result = NO_ERROR;
+ switch (nEncapsulationType) {
+ case AUDIO_ENCAPSULATION_TYPE_NONE:
+ *encapsulationType = ENCAPSULATION_TYPE_NONE;
+ break;
+ case AUDIO_ENCAPSULATION_TYPE_IEC61937:
+ *encapsulationType = ENCAPSULATION_TYPE_IEC61937;
+ break;
+ default:
+ result = BAD_VALUE;
+ }
+ return result;
+}
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_AUDIOPROFILE_H
\ No newline at end of file
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index f102edc..6f9a381 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -34,10 +34,12 @@
#include <system/audio.h>
#include <system/audio_policy.h>
#include "android_media_AudioAttributes.h"
+#include "android_media_AudioDescriptor.h"
#include "android_media_AudioDeviceAttributes.h"
#include "android_media_AudioEffectDescriptor.h"
#include "android_media_AudioErrors.h"
#include "android_media_AudioFormat.h"
+#include "android_media_AudioProfile.h"
#include "android_media_MicrophoneInfo.h"
// ----------------------------------------------------------------------------
@@ -176,6 +178,9 @@
static struct { jmethodID add; } gListMethods;
+static jclass gAudioDescriptorClass;
+static jmethodID gAudiODescriptorCstor;
+
//
// JNI Initialization for OpenSLES routing
//
@@ -1217,6 +1222,7 @@
jobject jAudioPortConfig = NULL;
jstring jDeviceName = NULL;
jobject jAudioProfiles = NULL;
+ jobject jAudioDescriptors = nullptr;
bool useInMask;
ALOGV("convertAudioPortFromNative id %d role %d type %d name %s",
@@ -1293,13 +1299,21 @@
}
}
+ int encapsulationType;
+ if (audioEncapsulationTypeFromNative(nAudioPort->audio_profiles[i].encapsulation_type,
+ &encapsulationType) != NO_ERROR) {
+ ALOGW("Unknown encapsualtion type for JAVA API: %u",
+ nAudioPort->audio_profiles[i].encapsulation_type);
+ continue;
+ }
+
ScopedLocalRef<jobject>
jAudioProfile(env,
env->NewObject(gAudioProfileClass, gAudioProfileCstor,
audioFormatFromNative(
nAudioPort->audio_profiles[i].format),
jSamplingRates.get(), jChannelMasks.get(),
- jChannelIndexMasks.get()));
+ jChannelIndexMasks.get(), encapsulationType));
if (jAudioProfile == nullptr) {
jStatus = (jint)AUDIO_JAVA_ERROR;
goto exit;
@@ -1307,6 +1321,42 @@
env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, jAudioProfile.get());
}
+ jAudioDescriptors = env->NewObject(gArrayListClass, gArrayListMethods.cstor);
+ if (jAudioDescriptors == nullptr) {
+ jStatus = (jint)AUDIO_JAVA_ERROR;
+ goto exit;
+ }
+ for (size_t i = 0; i < nAudioPort->num_extra_audio_descriptors; ++i) {
+ const auto &extraAudioDescriptor = nAudioPort->extra_audio_descriptors[i];
+ ScopedLocalRef<jobject> jAudioDescriptor(env);
+ if (extraAudioDescriptor.descriptor_length == 0) {
+ continue;
+ }
+ int standard;
+ if (audioStandardFromNative(extraAudioDescriptor.standard, &standard) != NO_ERROR) {
+ ALOGW("Unknown standard for JAVA API: %u", extraAudioDescriptor.standard);
+ continue;
+ }
+ int encapsulationType;
+ if (audioEncapsulationTypeFromNative(extraAudioDescriptor.encapsulation_type,
+ &encapsulationType) != NO_ERROR) {
+ ALOGW("Unknown encapsualtion type for JAVA API: %u",
+ extraAudioDescriptor.encapsulation_type);
+ continue;
+ }
+ ScopedLocalRef<jbyteArray> jDescriptor(env,
+ env->NewByteArray(
+ extraAudioDescriptor.descriptor_length));
+ env->SetByteArrayRegion(jDescriptor.get(), 0, extraAudioDescriptor.descriptor_length,
+ reinterpret_cast<const jbyte *>(extraAudioDescriptor.descriptor));
+ jAudioDescriptor =
+ ScopedLocalRef<jobject>(env,
+ env->NewObject(gAudioDescriptorClass, gAudiODescriptorCstor,
+ standard, encapsulationType,
+ jDescriptor.get()));
+ env->CallBooleanMethod(jAudioDescriptors, gArrayListMethods.add, jAudioDescriptor.get());
+ }
+
// gains
jGains = env->NewObjectArray(nAudioPort->num_gains,
gAudioGainClass, NULL);
@@ -1365,7 +1415,7 @@
*jAudioPort =
env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, jHandle, jDeviceName,
jAudioProfiles, jGains, nAudioPort->ext.device.type, jAddress,
- jEncapsulationModes, jEncapsulationMetadataTypes);
+ jEncapsulationModes, jEncapsulationMetadataTypes, jAudioDescriptors);
env->DeleteLocalRef(jAddress);
} else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
ALOGV("convertAudioPortFromNative is a mix");
@@ -1414,6 +1464,9 @@
if (jAudioPortConfig != NULL) {
env->DeleteLocalRef(jAudioPortConfig);
}
+ if (jAudioDescriptors != nullptr) {
+ env->DeleteLocalRef(jAudioDescriptors);
+ }
return jStatus;
}
@@ -2790,7 +2843,8 @@
gAudioDevicePortCstor =
GetMethodIDOrDie(env, audioDevicePortClass, "<init>",
"(Landroid/media/AudioHandle;Ljava/lang/String;Ljava/util/List;"
- "[Landroid/media/AudioGain;ILjava/lang/String;[I[I)V");
+ "[Landroid/media/AudioGain;ILjava/lang/String;[I[I"
+ "Ljava/util/List;)V");
// When access AudioPort as AudioDevicePort
gAudioPortFields.mType = GetFieldIDOrDie(env, audioDevicePortClass, "mType", "I");
@@ -2909,7 +2963,11 @@
jclass audioProfileClass = FindClassOrDie(env, "android/media/AudioProfile");
gAudioProfileClass = MakeGlobalRefOrDie(env, audioProfileClass);
- gAudioProfileCstor = GetMethodIDOrDie(env, audioProfileClass, "<init>", "(I[I[I[I)V");
+ gAudioProfileCstor = GetMethodIDOrDie(env, audioProfileClass, "<init>", "(I[I[I[II)V");
+
+ jclass audioDescriptorClass = FindClassOrDie(env, "android/media/AudioDescriptor");
+ gAudioDescriptorClass = MakeGlobalRefOrDie(env, audioDescriptorClass);
+ gAudiODescriptorCstor = GetMethodIDOrDie(env, audioDescriptorClass, "<init>", "(II[B)V");
AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback);
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 2e4be14..29f8ccf 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -487,9 +487,15 @@
}
void markVintf() {
+ AutoMutex _l(mLock);
mVintf = true;
}
+ void forceDowngradeToSystemStability() {
+ AutoMutex _l(mLock);
+ mVintf = false;
+ }
+
sp<IBinder> getExtension() {
AutoMutex _l(mLock);
sp<JavaBBinder> b = mBinder.promote();
@@ -1005,6 +1011,12 @@
jbh->markVintf();
}
+static void android_os_Binder_forceDowngradeToSystemStability(JNIEnv* env, jobject clazz) {
+ JavaBBinderHolder* jbh =
+ (JavaBBinderHolder*) env->GetLongField(clazz, gBinderOffsets.mObject);
+ jbh->forceDowngradeToSystemStability();
+}
+
static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz)
{
IPCThreadState::self()->flushCommands();
@@ -1069,6 +1081,7 @@
{ "clearCallingWorkSource", "()J", (void*)android_os_Binder_clearCallingWorkSource },
{ "restoreCallingWorkSource", "(J)V", (void*)android_os_Binder_restoreCallingWorkSource },
{ "markVintfStability", "()V", (void*)android_os_Binder_markVintfStability},
+ { "forceDowngradeToSystemStability", "()V", (void*)android_os_Binder_forceDowngradeToSystemStability},
{ "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
{ "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
{ "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f54ffc5..5f41105 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1460,6 +1460,27 @@
transaction->reparent(ctrl, newParent);
}
+static void nativeOverrideHdrTypes(JNIEnv* env, jclass clazz, jobject tokenObject,
+ jintArray jHdrTypes) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == nullptr || jHdrTypes == nullptr) return;
+
+ int* hdrTypes = env->GetIntArrayElements(jHdrTypes, 0);
+ int numHdrTypes = env->GetArrayLength(jHdrTypes);
+
+ std::vector<ui::Hdr> hdrTypesVector;
+ for (int i = 0; i < numHdrTypes; i++) {
+ hdrTypesVector.push_back(static_cast<ui::Hdr>(hdrTypes[i]));
+ }
+ env->ReleaseIntArrayElements(jHdrTypes, hdrTypes, 0);
+
+ status_t error = SurfaceComposerClient::overrideHdrTypes(token, hdrTypesVector);
+ if (error != NO_ERROR) {
+ jniThrowExceptionFmt(env, "java/lang/SecurityException",
+ "ACCESS_SURFACE_FLINGER is missing");
+ }
+}
+
static void nativeSetAutoLowLatencyMode(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
if (token == NULL) return;
@@ -1658,10 +1679,12 @@
jobject jJankData = env->NewObject(gJankDataClassInfo.clazz,
gJankDataClassInfo.ctor, jankData[i].frameVsyncId, jankData[i].jankType);
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
+ env->DeleteLocalRef(jJankData);
}
env->CallVoidMethod(target,
gJankDataListenerClassInfo.onJankDataAvailable,
jJankDataArray);
+ env->DeleteLocalRef(jJankDataArray);
env->DeleteLocalRef(target);
}
@@ -1819,6 +1842,8 @@
(void*)nativeSetGameContentType },
{"nativeGetCompositionDataspaces", "()[I",
(void*)nativeGetCompositionDataspaces},
+ {"nativeOverrideHdrTypes", "(Landroid/os/IBinder;[I)V",
+ (void*)nativeOverrideHdrTypes },
{"nativeClearContentFrameStats", "(J)Z",
(void*)nativeClearContentFrameStats },
{"nativeGetContentFrameStats", "(JLandroid/view/WindowContentFrameStats;)Z",
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index dca6002..530cb44 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -123,6 +123,8 @@
optional SettingProto gesture_silence_alerts_enabled = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto gesture_wake_enabled = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto gesture_setup_complete = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto touch_gesture_enabled = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto long_press_home_enabled = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Assist assist = 7;
@@ -296,6 +298,7 @@
optional SettingProto subtype_history = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto selected_input_method_subtype = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto show_ime_with_hard_keyboard = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto default_voice_input_method = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional InputMethods input_methods = 26;
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 0d23946..acb7429 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -58,6 +58,9 @@
optional bool is_screen_bright = 1;
optional bool is_screen_dim = 2;
optional bool is_screen_dream = 3;
+ optional int64 last_user_activity_time_ms = 4;
+ optional int64 last_user_activity_time_no_change_lights_ms = 5;
+ optional int32 display_group_id = 6;
}
// A com.android.server.power.PowerManagerService.UidState object.
message UidStateProto {
@@ -109,7 +112,7 @@
// The time we decided to do next long check. (In milliseconds timestamp)
optional int64 notify_long_next_check_ms = 19;
// Summarizes the effect of the user activity timer.
- optional UserActivityProto user_activity = 20;
+ repeated UserActivityProto user_activity = 20;
// If true, instructs the display controller to wait for the proximity
// sensor to go negative before turning the screen on.
optional bool is_request_wait_for_negative_proximity = 21;
@@ -134,8 +137,8 @@
// Timestamp of the last time the device was put to sleep.
optional int64 last_sleep_time_ms = 30;
// Timestamp of the last call to user activity.
- optional int64 last_user_activity_time_ms = 31;
- optional int64 last_user_activity_time_no_change_lights_ms = 32;
+ optional int64 last_user_activity_time_ms = 31 [deprecated = true];
+ optional int64 last_user_activity_time_no_change_lights_ms = 32 [deprecated = true];
// Timestamp of last interactive power hint.
optional int64 last_interactive_power_hint_time_ms = 33;
// Timestamp of the last screen brightness boost.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1d462bc..00b165e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -575,6 +575,7 @@
<protected-broadcast android:name="android.intent.action.MEDIA_RESOURCE_GRANTED" />
<protected-broadcast android:name="android.app.action.NETWORK_LOGS_AVAILABLE" />
<protected-broadcast android:name="android.app.action.SECURITY_LOGS_AVAILABLE" />
+ <protected-broadcast android:name="android.app.action.COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED" />
<protected-broadcast android:name="android.app.action.INTERRUPTION_FILTER_CHANGED" />
<protected-broadcast android:name="android.app.action.INTERRUPTION_FILTER_CHANGED_INTERNAL" />
@@ -1312,7 +1313,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_recordBackgroundAudio"
android:description="@string/permdesc_recordBackgroundAudio"
- android:protectionLevel="internal" />
+ android:protectionLevel="internal|role" />
<!-- ====================================================================== -->
<!-- Permissions for activity recognition -->
@@ -1404,7 +1405,7 @@
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_backgroundCamera"
android:description="@string/permdesc_backgroundCamera"
- android:protectionLevel="internal" />
+ android:protectionLevel="internal|role" />
<!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
system only camera devices.
@@ -2015,6 +2016,12 @@
android:label="@string/permlab_preferredPaymentInfo"
android:protectionLevel="normal" />
+ <!-- @SystemApi Allows access to set NFC controller always on states.
+ <p>Protection level: signature|privileged
+ @hide -->
+ <permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
Applications holding this permission can access OMAPI reset system API
and bypass OMAPI AccessControlEnforcer.
@@ -2689,6 +2696,11 @@
<permission android:name="android.permission.CREATE_USERS"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Allows an application to access data blobs across users.
+ This permission is not available to third party applications. -->
+ <permission android:name="android.permission.ACCESS_BLOBS_ACROSS_USERS"
+ android:protectionLevel="signature|privileged|development" />
+
<!-- @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
@@ -3998,6 +4010,15 @@
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
android:protectionLevel="signature" />
+ <!-- Allows an app to schedule a prioritized alarm that can be used to perform
+ background work even when the device is in doze.
+ <p>Not for use by third-party applications.
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.SCHEDULE_PRIORITIZED_ALARM"
+ android:protectionLevel="signature|privileged"/>
+
<!-- Allows an app to use exact alarm scheduling APIs to perform timing
sensitive background work.
-->
@@ -4227,6 +4248,11 @@
<permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE"
android:protectionLevel="normal" />
+ <!-- Allows an application to create new companion device associations.
+ @hide -->
+ <permission android:name="android.permission.ASSOCIATE_COMPANION_DEVICES"
+ android:protectionLevel="internal|role" />
+
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide
@@ -5424,12 +5450,6 @@
<permission android:name="android.permission.WATCH_APPOPS"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to directly open the "Open by default" page inside a package's
- Details screen.
- @hide <p>Not for use by third-party applications. -->
- <permission android:name="android.permission.OPEN_APP_OPEN_BY_DEFAULT_SETTINGS"
- android:protectionLevel="signature" />
-
<!-- Allows hidden API checks to be disabled when starting a process.
@hide <p>Not for use by third-party applications. -->
<permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS"
@@ -5556,9 +5576,17 @@
<permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"
android:protectionLevel="signature|privileged" />
<!-- Allows an app to override compat change config.
+ This permission only allows to override config on debuggable builds or test-apks and is
+ therefore a less powerful version of OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"
android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
+ <!-- @SystemApi Allows an app to override compat change config on release builds.
+ Only ChangeIds that are annotated as @Overridable can be overridden on release builds.
+ @hide -->
+ <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"
+ android:protectionLevel="signature|privileged" />
<!-- Allows input events to be monitored. Very dangerous! @hide -->
<permission android:name="android.permission.MONITOR_INPUT"
@@ -5660,12 +5688,31 @@
<permission android:name="android.permission.RENOUNCE_PERMISSIONS"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to read nearby streaming policy. The policy allows the device
+ to stream its notifications and apps to nearby devices.
+ @hide -->
+ <permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows the holder to set the source of the data when setting a clip on the
clipboard.
@hide -->
<permission android:name="android.permission.SET_CLIP_SOURCE"
android:protectionLevel="signature|recents" />
+ <!-- Allows an application to request installs that update existing packages do so without
+ user action via
+ {@link android.content.pm.PackageInstaller.SessionParams#setRequireUserAction(boolean)}.
+ This permission only grants the ability to make the request and is not a guarantee that the
+ request will be honored. In order to execute the install, the caller must also have the
+ "android.permission.REQUEST_INSTALL_PACKAGES" or "android.permission.INSTALL_PACKAGES"
+ permissions.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"
+ android:protectionLevel="normal" />
+ <uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"/>
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/layout/chooser_action_button.xml b/core/res/res/layout/chooser_action_button.xml
index 16ffaa4..def429e 100644
--- a/core/res/res/layout/chooser_action_button.xml
+++ b/core/res/res/layout/chooser_action_button.xml
@@ -19,13 +19,13 @@
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:drawablePadding="8dp"
- android:textColor="?android:textColorPrimary"
+ android:textColor="@color/text_color_primary_device_default_light"
android:textSize="12sp"
android:maxWidth="192dp"
android:singleLine="true"
android:clickable="true"
android:background="@drawable/chooser_action_button_bg"
- android:drawableTint="?android:textColorPrimary"
+ android:drawableTint="@color/text_color_primary_device_default_light"
android:drawableTintMode="src_in"
style="?android:attr/borderlessButtonStyle"
/>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index a1fb14f..39de289 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Program loop tans"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programme wat batterykrag gebruik"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Vergroting"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Toeganklikheidsekuriteitbeleid"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> gebruik tans batterykrag"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> programme gebruik tans batterykrag"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tik vir besonderhede oor battery- en datagebruik"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gebruik kortpad"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Kleuromkering"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurkorreksie"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Verminder helderheid"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra donker"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is afgeskakel"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Druk en hou albei volumesleutels drie sekondes lank om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruik"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorprivaatheid"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Programikoon"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Programhandelsmerkprent"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Gaan toeganginstellings na"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> kan jou skerm sien en beheer. Tik om na te gaan."</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 2bc9f6e..b8af80f 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"APP እየሠራ ነው"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"ባትሪ በመፍጀት ላይ ያሉ መተግበሪያዎች"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"ማጉላት"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"የተደራሽነት ደህንነት መመሪያ"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ባትሪ እየተጠቀመ ነው"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> መተግበሪያዎች ባትሪ እየተጠቀሙ ነው"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"በባትሪ እና ውሂብ አጠቃቀም ላይ ዝርዝሮችን ለማግኘት መታ ያድርጉ"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"አቋራጭ ይጠቀሙ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ተቃራኒ ቀለም"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"የቀለም ማስተካከያ"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"ብሩህነትን ይቀንሱ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"የበለጠ ደብዛዛ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> በርቷል።"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ጠፍተዋል።"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን ለመጠቀም ለሦስት ሰከንዶች ሁለቱንም የድምፅ ቁልፎች ተጭነው ይያዙ"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ዳሳሽ ግላዊነት"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"የመተግበሪያ አዶ"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"የመተግበሪያ የምርት ስም ምስል"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"የመዳረሻ ቅንብሮችን ይፈትሹ"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ማያ ገጽዎን ማየት እና መቆጣጠር ይችላል። ለመገምገም መታ ያድርጉ።"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 8f49288..fc94e1c 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -306,8 +306,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"التطبيق قيد التشغيل"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"التطبيقات التي تستهلك البطارية"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"التكبير"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"سياسة أمان \"تسهيل الاستخدام\""</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"يستخدم تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> البطارية"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"تستخدم <xliff:g id="NUMBER">%1$d</xliff:g> من التطبيقات البطارية"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"انقر للحصول على تفاصيل حول البطارية واستخدام البيانات"</string>
@@ -1778,7 +1777,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استخدام الاختصار"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"قلب الألوان"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"تصحيح الألوان"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"تقليل السطوع"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (3222994553174604132) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم تفعيل <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم إيقاف <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"اضغط مع الاستمرار على مفتاحي مستوى الصوت لمدة 3 ثوانٍ لاستخدام <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -2383,8 +2383,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"الخصوصية في جهاز الاستشعار"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"رمز التطبيق"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"الصورة الذهنية للعلامة التجارية للتطبيق"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"التحقّق من إعدادات الوصول"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"يمكن لخدمة <xliff:g id="SERVICE_NAME">%s</xliff:g> الاطّلاع على شاشتك والتحكّم فيها. انقر لمراجعة الإعدادات."</string>
</resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index a34ac67..caebacf 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"এপ্ চলি আছে"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"বেটাৰি খৰচ কৰা এপসমূহ"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"বিবৰ্ধন"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"সাধ্য সুবিধাৰ সুৰক্ষা নীতি"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বেটাৰি ব্যৱহাৰ কৰি আছে"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>টা এপে বেটাৰি ব্যৱহাৰ কৰি আছে"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"বেটাৰি আৰু ডেটাৰ ব্যৱহাৰৰ বিষয়ে বিশদভাৱে জানিবলৈ টিপক"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শ্বৰ্টকাট ব্যৱহাৰ কৰক"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ৰং বিপৰীতকৰণ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ৰং শুধৰণী"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"উজ্জ্বলতা কমাওক"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিৰিক্তভাৱে অনুজ্জ্বল"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কীসমূহ ধৰি ৰাখক। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অন কৰা হ\'ল।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধৰি ৰাখিছিল। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অফ কৰা হ\'ল।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ব্যৱহাৰ কৰিবলৈ দুয়োটা ভলিউম বুটাম তিনি ছেকেণ্ডৰ বাবে হেঁচি ৰাখক"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ছেন্সৰ সম্পৰ্কীয় গোপনীয়তাৰ নীতি"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"এপ্লিকেশ্বনৰ চিহ্ন"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"এপ্লিকেশ্বনৰ ব্ৰেণ্ডৰ প্ৰতিচ্ছবি"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"এক্সেছৰ ছেটিং পৰীক্ষা কৰক"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g>এ আপোনাৰ স্ক্ৰীনখন চাব আৰু পৰিচালনা কৰিব পাৰে। পৰ্যালোচনা কৰিবলৈ টিপক।"</string>
</resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 993a20e..948b274 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Tətbiq işləyir"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Batareyadan istifadə edən tətbiqlər"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Böyütmə"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Əlçatımlılıq güvənlik siyasəti"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> batareyadan istifadə edir"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> tətbiq batareyadan istifadə edir"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Batareya və data istifadəsi haqqında ətraflı məlumat üçün klikləyin"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Qısayol İstifadə edin"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Rəng İnversiyası"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Rəng korreksiyası"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Parlaqlığı azaldın"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Əlavə qaraltma"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiv edildi."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basılaraq saxlanıb. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edilib."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> istifadə etmək üçün hər iki səs düyməsini üç saniyə basıb saxlayın"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensor Məxfiliyi"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Tətbiq ikonası"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Tətbiqin brend şəkli"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Giriş ayarlarını yoxlayın"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ekranınıza baxa və nəzarət edə bilər. Nəzərdən keçirmək üçün toxunun."</string>
</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 80baf41..9c18a07 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1711,7 +1711,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boja"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Smanjite osvetljenost"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatno zatamnjeno"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite oba tastera za jačinu zvuka tri sekunde da biste koristili <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 3ac18b3..5175f47 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -300,8 +300,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Праграма працуе"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Праграмы, якія выкарыстоўваюць акумулятар"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Павелічэнне"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Палітыка бяспекі спецыяльных магчымасцей"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> выкарыстоўвае акумулятар"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Наступная колькасць праграм выкарыстоўваюць акумулятар: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Дакраніцеся, каб даведацца пра выкарыстанне трафіка і акумулятара"</string>
@@ -1734,7 +1733,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Выкарыстоўваць камбінацыю хуткага доступу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія колеру"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Карэкцыя колеру"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Паменшыць яркасць"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дадатковае памяншэнне яркасці"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Клавішы гучнасці ўтрымліваліся націснутымі. Уключана служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Клавішы гучнасці ўтрымліваліся націснутымі. Служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" выключана."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Каб карыстацца сэрвісам \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", націсніце і ўтрымлівайце на працягу трох секунд абедзве клавішы гучнасці"</string>
@@ -2315,8 +2314,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Прыватнасць інфармацыі з датчыка"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Значок праграмы"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Відарыс брэнда праграмы"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Праверце налады доступу"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> можа праглядаць экран вашай прылады і кіраваць ім. Націсніце, каб праглядзець."</string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 7aad6b9..0b3e04a 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Приложението работи"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Приложения, използващи батерията"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Увеличение"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Правила за сигурност на услугите за достъпност"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> използва батерията"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> приложения използват батерията"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Докоснете за информация относно използването на батерията и преноса на данни"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Използване на пряк път"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инвертиране на цветовете"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Коригиране на цветовете"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Намаляване на яркостта"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Допълнително затъмняване"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е включена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е изключена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"За да използвате <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, натиснете двата бутона за силата на звука и ги задръжте за 3 секунди"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Поверителност на сензорните данни"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Икона на приложението"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Изображение на търговската марка на приложението"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Проверете настройките за достъп"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> може да преглежда и управлява съдържанието на екрана ви. Докоснете за преглед."</string>
</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 79d07ca..678cb21 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"অ্যাপ চলছে"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"কিছু অ্যাপ ব্যাটারি ব্যবহার করছে"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"বড় করে দেখুন"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"অ্যাক্সেসিবিলিটি সুরক্ষা নীতি"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপটি ব্যাটারি ব্যবহার করছে"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>টি অ্যাপ ব্যাটারি ব্যবহার করছে"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ব্যাটারি এবং ডেটার ব্যবহারের বিশদ বিবরণের জন্য ট্যাপ করুন"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শর্টকাট ব্যবহার করুন"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"রঙ উল্টানো"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"রঙ সংশোধন"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"উজ্জ্বলতা কমান"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিরিক্ত কম আলো"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করা হয়েছে।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ব্যবহার করতে ভলিউম কী বোতাম ৩ সেকেন্ডের জন্য চেপে ধরে রাখুন"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"সেন্সর গোপনীয়তা"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"অ্যাপের আইকন"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"অ্যাপের ব্র্যান্ড ছবি"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"অ্যাক্সেস করার সেটিংস চেক করুন"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> আপনার স্ক্রিন দেখতে ও কন্ট্রোল করতে পারবে। পর্যালোচনা করতে ট্যাপ করুন।"</string>
</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 1175ab0..d6e0f32 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1711,7 +1711,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Ispravka boja"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Smanjenje osvjetljenja"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatno zatamnjenje"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite obje tipke za podešavanje jačine zvuka i držite ih pritisnutim tri sekunde da koristite uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2281,5 +2281,5 @@
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikona aplikacije"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Slika robne marke za aplikaciju"</string>
<string name="view_and_control_notification_title" msgid="4300765399209912240">"Provjerite postavke pristupa"</string>
- <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> može pregledavati i kontrolirati vaš zaslon. Dodirnite za pregled."</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> može pregledati i kontrolirati vaš ekran. Dodirnite da pregledate."</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index dd1001c..5f2904c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicació en execució"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicacions que consumeixen bateria"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ampliació"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Política de seguretat de l\'accessibilitat"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> està consumint bateria"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicacions estan consumint bateria"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toca per obtenir informació sobre l\'ús de dades i de bateria"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilitza la drecera"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversió de colors"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correcció de color"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reducció de la brillantor"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extratènue"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S\'han mantingut premudes les tecles de volum. S\'ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S\'han mantingut premudes les tecles de volum. S\'ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén premudes les dues tecles de volum durant 3 segons per fer servir <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2085,7 +2084,7 @@
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> i <xliff:g id="COUNT_3">%d</xliff:g> fitxers més</item>
<item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> i <xliff:g id="COUNT_1">%d</xliff:g> fitxer més</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"No hi ha cap recomanació de persones amb qui compartir"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"No hi ha cap suggeriment de persones amb qui compartir"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Llista d\'aplicacions"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Aquesta aplicació no té permís de gravació, però pot capturar àudio a través d\'aquest dispositiu USB."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Inici"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privadesa dels sensors"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Icona d\'aplicació"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imatge de brànding de l\'aplicació"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Comprova la configuració d\'accés"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> pot veure i controlar la teva pantalla. Toca per revisar-ho."</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index e90778d..6732b96 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -300,8 +300,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikace je spuštěna"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikace spotřebovávají baterii"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Zvětšení"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Zásady zabezpečení přístupnosti"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> využívá baterii"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Aplikace (<xliff:g id="NUMBER">%1$d</xliff:g>) využívají baterii"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Klepnutím zobrazíte podrobnosti o využití baterie a dat"</string>
@@ -1734,7 +1733,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použít zkratku"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Převrácení barev"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Oprava barev"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Snížit jas"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Velmi tmavé"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> byla vypnuta."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Chcete-li používat službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tři sekundy podržte stisknutá obě tlačítka hlasitosti"</string>
@@ -2315,8 +2314,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Ochrana soukromí – senzor"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikona aplikace"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Image značky aplikace"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Zkontrolujte nastavení přístupu"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> může zobrazit a ovládat tuto obrazovku. Klepnutím to zkontrolujete."</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 614d849..9dcb4d5 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Appen kører"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps, der bruger batteri"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Forstørrelse"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Sikkerhedspolitik for hjælpefunktioner"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> bruger batteri"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps bruger batteri"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tryk for at se info om batteri- og dataforbrug"</string>
@@ -1692,7 +1691,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Brug genvej"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ombytning af farver"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korriger farve"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reducer lysstyrken"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra dæmpet belysning"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er aktiveret."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er deaktiveret."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Hold begge lydstyrkeknapper nede i tre sekunder for at bruge <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2249,8 +2248,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Beskyttelse af sensoroplysninger"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Appens ikon"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Appens brandimage"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Tjek adgangsindstillingerne"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> kan se og styre din skærm. Tryk for at se mere."</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index eb08d7a..60cb51d 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App wird ausgeführt"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Strom verbrauchende Apps"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Vergrößerung"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Sicherheitsrichtlinien für Bedienungshilfen"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> verbraucht Strom"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> Apps verbrauchen Strom"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Für Details zur Akku- und Datennutzung tippen"</string>
@@ -1690,7 +1689,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Verknüpfung verwenden"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Farbumkehr"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Farbkorrektur"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Helligkeit verringern"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (3222994553174604132) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist aktiviert."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist deaktiviert."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Halten Sie beide Lautstärketasten drei Sekunden lang gedrückt, um <xliff:g id="SERVICE_NAME">%1$s</xliff:g> zu verwenden"</string>
@@ -2247,8 +2247,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Datenschutz für Sensoren"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"App-Symbol"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"App-Branding-Hintergrundbild"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Zugriffseinstellungen prüfen"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> kann deinen Bildschirm sehen und steuern. Zum Prüfen tippen."</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 701ebe4..d2ea1e6 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Η εφαρμογή εκτελείται"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Εφαρμογές που καταναλώνουν μπαταρία"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Μεγιστοποίηση"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Πολιτική ασφαλείας προσβασιμότητας"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> χρησιμοποιεί μπαταρία"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> εφαρμογές χρησιμοποιούν μπαταρία"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Πατήστε για λεπτομέρειες σχετικά με τη χρήση μπαταρίας και δεδομένων"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Χρήση συντόμευσης"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Αντιστροφή χρωμάτων"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Διόρθωση χρωμάτων"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Μείωση φωτεινότητας"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Επιπλέον μείωση φωτεινότητας"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ενεργοποιήθηκε."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>: απενεργοποιημένο"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Πατήστε παρατεταμένα και τα δύο κουμπιά έντασης ήχου για τρία δευτερόλεπτα, ώστε να χρησιμοποιήσετε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Απόρρητο αισθητήρα"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Εικονίδιο εφαρμογής"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Εικόνα επωνυμίας εφαρμογής"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Ελέγξτε τις ρυθμίσεις προσβασιμότητας"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"Η υπηρεσία <xliff:g id="SERVICE_NAME">%s</xliff:g> μπορεί να βλέπει και να ελέγχει την οθόνη σας. Πατήστε για έλεγχο."</string>
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index c58424f..d5bc66d 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduce brightness"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index d4afff1..11d1780 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduce brightness"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index e4951d7..4e15e60 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduce brightness"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 9f4baf9..60d743b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Color correction"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduce brightness"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index ccbe24e..a4bf0c3 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Color Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Color Correction"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduce brightness"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 938c6d7..da9292a 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App en ejecución"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que consumen batería"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ampliación"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Política de seguridad de accesibilidad"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> está consumiendo batería"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps están consumiendo batería"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Presiona para obtener información sobre el uso de datos y de la batería"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar acceso directo"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reducir el brillo"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuación extra"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Como mantuviste presionadas las teclas de volumen, se activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se presionaron las teclas de volumen. Se desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén presionadas ambas teclas de volumen durante tres segundos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidad del sensor"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ícono de la aplicación"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imagen de marca de la aplicación"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Verifica la configuración de acceso"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> puede ver y controlar tu pantalla. Presiona para revisar esta opción."</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 65c2e68..ce2ee37 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicación en ejecución"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicaciones que consumen batería"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ampliación"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Política de seguridad de accesibilidad"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> está usando la batería"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicaciones están usando la batería"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toca para ver información detallada sobre el uso de datos y de la batería"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar acceso directo"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reducir brillo"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuación extra"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Al mantener pulsadas las teclas de volumen, se ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se han mantenido pulsadas las teclas de volumen. Se ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Para utilizar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantén pulsadas ambas teclas de volumen durante 3 segundos"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidad del sensor"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Icono de aplicación"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imagen de marca de aplicación"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Comprueba los ajustes de acceso"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> puede ver y controlar tu pantalla. Toca para revisarlo."</string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 202fd44..04ab1f3 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Rakendus töötab"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Rakendused kasutavad akutoidet"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Suurendus"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Juurdepääsufunktsioonide turvaeeskirjad"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> kasutab akutoidet"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> rakendust kasutab akutoidet"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Aku ja andmekasutuse üksikasjade nägemiseks puudutage"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kasuta otseteed"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Värvide ümberpööramine"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Värvide korrigeerimine"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Ereduse vähendamine"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Eriti tume"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kasutamiseks hoidke kolm sekundit all mõlemat helitugevuse klahvi"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Anduri privaatsus"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Rakenduse ikoon"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Rakenduse brändi kujutis"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Kontrollige juurdepääsuseadeid"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> saab vaadata ja hallata teie ekraanikuva. Puudutage ülevaatamiseks."</string>
</resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 16da156..2677dfd 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikazio bat abian da"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Bateria kontsumitzen ari diren aplikazioak"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Lupa"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Irisgarritasunari buruzko segurtasun-gidalerroak"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ari da bateria erabiltzen"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikazio ari dira bateria erabiltzen"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Sakatu bateria eta datuen erabilerari buruzko xehetasunak ikusteko"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Erabili lasterbidea"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Koloreen alderantzikatzea"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Koloreen zuzenketa"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Murriztu distira"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Are ilunago"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatu egin da."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desaktibatu egin da."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> erabiltzeko, eduki sakatuta bi bolumen-botoiak hiru segundoz"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sentsoreen pribatutasuna"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Aplikazioaren ikonoa"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Aplikazioaren marka-irudia"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Begiratu sarbide-ezarpenak"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> zerbitzuak pantaila ikusi eta kontrola dezake. Sakatu berrikusteko."</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index a1f8f5b..19d6256 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"برنامه درحال اجرا"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"برنامههای مصرفکننده باتری"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"درشتنمایی"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"خطمشی امنیتی دسترسپذیری"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> درحال استفاده کردن از باتری است"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> برنامه درحال استفاده کردن از باتری هستند"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"برای جزئیات مربوط به مصرف باتری و داده، ضربه بزنید"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استفاده از میانبر"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"وارونگی رنگ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"تصحیح رنگ"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"کاهش روشنایی"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"بسیار کمنور"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> روشن شد."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"برای استفاده از <xliff:g id="SERVICE_NAME">%1$s</xliff:g>، هر دو کلید صدا را فشار دهید و سه ثانیه نگه دارید"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"حریمخصوصی حسگر"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"نماد برنامه"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"تصویر نمانامسازی برنامه"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"بررسی تنظیمات دسترسی"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> میتواند صفحهنمایش شما را مشاهده و کنترل کند. برای مرور، ضربه بزنید."</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index fb1a8fa..d78dffc 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Sovellus käynnissä"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Akkua kuluttavat sovellukset"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Suurennus"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Esteettömyyden tietoturvakäytäntö"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> käyttää akkua."</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> sovellusta käyttää akkua."</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Katso lisätietoja akun ja datan käytöstä napauttamalla."</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Käytä pikanäppäintä"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Käänteiset värit"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Värinkorjaus"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Vähennä kirkkautta"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Erittäin himmeä"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin päälle."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin pois päältä."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Voit käyttää palvelua <xliff:g id="SERVICE_NAME">%1$s</xliff:g> painamalla molempia äänenvoimakkuuspainikkeita kolmen sekunnin ajan"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Anturin tietosuoja"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Sovelluskuvake"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Sovelluksen tuotemerkkikuva"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Tarkista pääsyasetukset"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> voi nähdä ja ohjata näyttöäsi. Tarkista napauttamalla."</string>
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 76b40bd..3f18daf 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Application en cours d\'exécution"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Applications qui sollicitent la pile"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Agrandissement"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Politique de sécurité relative aux fonctionnalités d\'accessibilité"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> sollicite la pile"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> applications sollicitent la pile"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Touchez pour afficher des détails sur l\'utilisation de la pile et des données"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Réduire la luminosité"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Réduction supplémentaire de la luminosité"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Maintenez enfoncées les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidentialité des capteurs"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Icône de l\'application"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Image de marque de l\'application"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Vérifiez les paramètres d\'accès"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> peut voir et contrôler votre écran. Touchez pour examiner."</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 6d3c6ed..10a3bad 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Application en cours d\'exécution"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Applications utilisant la batterie"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Agrandissement"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Règlement sur la sécurité de l\'accessibilité"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> utilise la batterie"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> applications utilisent la batterie"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Appuyer pour obtenir des informations sur l\'utilisation de la batterie et des données"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Réduire la luminosité"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Encore moins lumineux"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Appuyez de manière prolongée sur les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidentialité du capteur"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Icône de l\'application"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Image de branding de l\'application"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Vérifiez les paramètres d\'accès"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> peut afficher et contrôler votre écran. Appuyez ici pour en savoir plus."</string>
</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 7adb258..0aac641 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Estase executando a aplicación"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicacións que consomen batería"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ampliación"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Política de seguranza relativa á accesibilidade"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> está consumindo batería"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicacións están consumindo batería"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toca para obter información sobre o uso de datos e a batería"</string>
@@ -1690,7 +1689,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar atallo"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de cor"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de cor"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reducir brillo"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (3222994553174604132) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume premidas. Activouse o servizo <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Desactivouse <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén premidas as teclas do volume durante tres segudos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2247,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidade do sensor"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Icona de aplicación"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imaxe de marca da aplicación"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Comproba a configuración do acceso"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"Agora <xliff:g id="SERVICE_NAME">%s</xliff:g> pode ver e controlar a túa pantalla. Toca para revisalo."</string>
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 3b69c71..40af808 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ઍપ ચાલી રહ્યું છે"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"ઍપ બૅટરીનો વપરાશ કરી રહ્યાં છે"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"મોટું કરવાની સુવિધા"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"ઍક્સેસિબિલિટી સુરક્ષા પૉલિસી"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> બૅટરીનો ઉપયોગ કરી રહ્યું છે"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ઍપ બૅટરીનો ઉપયોગ કરી રહ્યાં છે"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"બૅટરી અને ડેટા વપરાશ વિશેની વિગતો માટે ટૅપ કરો"</string>
@@ -1690,7 +1689,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"શૉર્ટકટનો ઉપયોગ કરો"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"વિપરીત રંગમાં બદલવું"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"રંગ સુધારણા"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"બ્રાઇટનેસ ઘટાડો"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (3222994553174604132) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>નો ઉપયોગ કરવા માટે બન્ને વૉલ્યૂમ કીને ત્રણ સેકન્ડ સુધી દબાવી રાખો"</string>
@@ -2247,8 +2247,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"સેન્સર પ્રાઇવસી સંબંધિત નોટિફિકેશન"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ઍપ્લિકેશનનું આઇકન"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ઍપ્લિકેશનની બ્રાંડિંગ છબી"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"ઍક્સેસના સેટિંગ ચેક કરો"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> તમારી સ્ક્રીન જોઈ અને નિયંત્રિત કરી શકે છે. રિવ્યૂ કરવા માટે ટૅપ કરો."</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 3c17dc3..20f703a 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ऐप अभी इस्तेमाल हो रहा है"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"बैटरी की खपत करने वाले ऐप"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"ज़ूम करने की सुविधा"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"सुलभता सुविधाओं से जुड़ी सुरक्षा नीति"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> बैटरी का इस्तेमाल कर रहा है"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ऐप बैटरी का इस्तेमाल कर रहे हैं"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"बैटरी और डेटा खर्च की जानकारी के लिए छूएं"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट का उपयोग करें"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रंग बदलने की सुविधा"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रंग में सुधार करने की सुविधा"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"स्क्रीन की चमक कम करें"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को चालू कर दिया गया."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को बंद कर दिया गया."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> इस्तेमाल करने के लिए आवाज़ वाले दोनों बटन तीन सेकंड तक दबाकर रखें"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेंसर से जुड़ी निजता के बारे में सूचना"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ऐप्लिकेशन का आइकॉन"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ऐप्लिकेशन की ब्रैंड इमेज"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"ऐक्सेस से जुड़ी सेटिंग देखें"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> आपकी स्क्रीन को देख सकता है और कंट्रोल कर सकता है. ऐक्सेस की समीक्षा करने के लिए टैप करें."</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 48a9bd7..25db5e6 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1711,7 +1711,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Upotrijebi prečac"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boje"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Smanjenje svjetline"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatno tamno"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za glasnoću. Uključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za glasnoću. Isključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite tipke za glasnoću na tri sekunde da biste koristili uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index f4edfce..c25e691 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Jelenleg futó alkalmazás"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Akkumulátort használó alkalmazások"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Nagyítás"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Kisegítő lehetőségek biztonsági házirendje"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás használja az akkumulátort"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> alkalmazás használja az akkumulátort"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Koppintson az akkumulátor- és adathasználat részleteinek megtekintéséhez"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Billentyűparancs használata"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Színek invertálása"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Színkorrekció"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Fényerő csökkentése"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extrasötét"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolva."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kikapcsolva."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"A(z) <xliff:g id="SERVICE_NAME">%1$s</xliff:g> használatához tartsa lenyomva három másodpercig a két hangerőgombot"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Érzékelőkkel kapcsolatos adatvédelem"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Alkalmazás ikonja"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Alkalmazás márkaképe"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Ellenőrizze a hozzáférési beállításokat"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"A(z) <xliff:g id="SERVICE_NAME">%s</xliff:g> megtekintheti és irányíthatja képernyőjét. Koppintson az áttekintéshez."</string>
</resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index d7a5f23..594b029 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Հավելվածն աշխատում է"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Մարտկոցի լիցքը ծախսող հավելվածներ"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Խոշորացում"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Հատուկ գործառույթների անվտանգության քաղաքականություն"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"«<xliff:g id="APP_NAME">%1$s</xliff:g>» հավելվածը ծախսում է մարտկոցի լիցքը"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> հավելված ծախսում է մարտկոցի լիցքը"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Հպեք՝ մարտկոցի և թրաֆիկի մանրամասները տեսնելու համար"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Օգտագործել դյուրանցումը"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Գունաշրջում"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Գունաշտկում"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Պայծառության նվազեցում"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Հավելյալ խամրեցում"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը միացավ։"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունն անջատվեց։"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"«<xliff:g id="SERVICE_NAME">%1$s</xliff:g>» ծառայությունն օգտագործելու համար սեղմեք և 3 վայրկյան պահեք ձայնի ուժգնության երկու կոճակները"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Տվիչների գաղտնիություն"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Հավելվածի պատկերակ"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Հավելվածի բրենդային պատկեր"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Ստուգեք մուտքի կարգավորումները"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ծառայությունը կարող է դիտել և կառավարել ձեր էկրանի բովանդակությունը։ Հպեք՝ մանրամասներն իմանալու համար։"</string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 3e9442d..00c8b5b 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikasi berjalan"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikasi yang menggunakan baterai"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Pembesaran"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Kebijakan keamanan aksesibilitas"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang menggunakan baterai"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikasi sedang meggunakan baterai"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ketuk untuk melihat detail penggunaan baterai dan data"</string>
@@ -329,7 +328,7 @@
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Mengambil konten jendela"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Memeriksa konten jendela tempat Anda berinteraksi."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Mengaktifkan Jelajahi dengan Sentuhan"</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Item yang diketuk akan diucapkan dengan jelas dan layar dapat dijelajahi menggunakan isyarat."</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Item yang diketuk akan diucapkan dengan jelas dan layar dapat dijelajahi menggunakan gestur."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Mengamati teks yang Anda ketik"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Meliputi data pribadi seperti nomor kartu kredit dan sandi."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Mengontrol perbesaran layar"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gunakan Pintasan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversi Warna"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Koreksi Warna"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Kurangi kecerahan"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra redup"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> diaktifkan."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dinonaktifkan."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tekan dan tahan kedua tombol volume selama tiga detik untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privasi Sensor"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikon aplikasi"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Brand image aplikasi"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Periksa setelan akses"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> dapat melihat dan mengontrol layar Anda. Ketuk untuk meninjau."</string>
</resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 6e3965f..1c5809f 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Forrit er í gangi"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Forrit sem nota rafhlöðuorku"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Stækkun"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Öryggisregla aðgengis"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> notar rafhlöðuorku"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> forrit nota rafhlöðuorku"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ýttu til að fá upplýsingar um rafhlöðu- og gagnanotkun"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Nota flýtileið"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Umsnúningur lita"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Litaleiðrétting"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Minnka birtu"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mjög dökkt"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Hljóðstyrkstökkum haldið inni. Kveikt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Hljóðstyrkstökkum haldið inni. Slökkt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Haltu báðum hljóðstyrkstökkunum inni í þrjár sekúndur til að nota <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Persónuvernd skynjara"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Forritstákn"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Mynd af merki forrits"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Skoða aðgangsstillingar"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> getur skoðað og stjórnað skjánum hjá þér. Ýttu til að skoða."</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4b00539..4e342b6 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usa scorciatoia"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversione dei colori"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correzione del colore"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Riduci la luminosità"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Attenuazione extra"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> attivato."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> disattivato."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tieni premuti entrambi i tasti del volume per tre secondi per utilizzare <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index d0e2e8e..f2f6138 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -34,7 +34,7 @@
<string name="defaultMsisdnAlphaTag" msgid="2285034592902077488">"MSISDN1"</string>
<string name="mmiError" msgid="2862759606579822246">"בעיה בחיבור או קוד MMI לא חוקי."</string>
<string name="mmiFdnError" msgid="3975490266767565852">"הפעולה מוגבלת למספרי חיוג קבועים בלבד."</string>
- <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"לא ניתן לשנות את הגדרות העברת השיחות מהטלפון שלך כשאתה במצב נדידה."</string>
+ <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"לא ניתן לשנות את הגדרות העברת השיחות מהטלפון במצב נדידה."</string>
<string name="serviceEnabled" msgid="7549025003394765639">"השירות הופעל."</string>
<string name="serviceEnabledFor" msgid="1463104778656711613">"השירות הופעל עבור:"</string>
<string name="serviceDisabled" msgid="641878791205871379">"השירות הושבת."</string>
@@ -45,7 +45,7 @@
<string name="badPin" msgid="888372071306274355">"קוד הגישה הישן שהקלדת שגוי."</string>
<string name="badPuk" msgid="4232069163733147376">"ה-PUK שהקלדת שגוי."</string>
<string name="mismatchPin" msgid="2929611853228707473">"קודי הגישה שהקלדת לא תואמים."</string>
- <string name="invalidPin" msgid="7542498253319440408">"הקלד קוד גישה שאורכו 4 עד 8 ספרות."</string>
+ <string name="invalidPin" msgid="7542498253319440408">"יש להקליד קוד אימות שאורכו 4 עד 8 ספרות."</string>
<string name="invalidPuk" msgid="8831151490931907083">"הקלד PUK באורך 8 מספרים או יותר."</string>
<string name="needPuk" msgid="7321876090152422918">"כרטיס ה-SIM נעול באמצעות PUK. הקלד את קוד PUK כדי לבטל את נעילתו."</string>
<string name="needPuk2" msgid="7032612093451537186">"הקלד PUK2 כדי לבטל את חסימת כרטיס ה-SIM."</string>
@@ -86,7 +86,7 @@
<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="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"אפשר לנסות לשנות את הרשת המועדפת. יש להקיש כדי לשנות אותה."</string>
<string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"שיחות חירום לא זמינות"</string>
<string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"לא ניתן לבצע שיחות חירום דרך Wi-Fi"</string>
<string name="notification_channel_network_alert" msgid="4788053066033851841">"התראות"</string>
@@ -124,7 +124,7 @@
<string name="roamingText11" msgid="5245687407203281407">"מודעת באנר נודדת מופעלת"</string>
<string name="roamingText12" msgid="673537506362152640">"מודעת באנר נודדת כבויה"</string>
<string name="roamingTextSearching" msgid="5323235489657753486">"מחפש שירות"</string>
- <string name="wfcRegErrorTitle" msgid="3193072971584858020">"לא ניתן היה להגדיר שיחות Wi-Fi"</string>
+ <string name="wfcRegErrorTitle" msgid="3193072971584858020">"לא ניתן היה להגדיר את התכונה \'שיחות Wi-Fi\'"</string>
<string-array name="wfcOperatorErrorAlertMessages">
<item msgid="468830943567116703">"כדי להתקשר ולשלוח הודעות ברשת Wi-Fi, תחילה יש לבקש מהספק להגדיר את השירות. לאחר מכן, יש להפעיל שוב שיחות Wi-Fi ב\'הגדרות\'. (קוד שגיאה: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
@@ -180,7 +180,7 @@
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"נעשה ניסיון למחוק יותר מדי <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="5557552311566179924">"שטח האחסון של הטאבלט מלא. מחק קבצים כדי לפנות מקום."</string>
<string name="low_memory" product="watch" msgid="3479447988234030194">"שטח האחסון של השעון מלא. מחק כמה קבצים כדי לפנות שטח."</string>
- <string name="low_memory" product="tv" msgid="6663680413790323318">"האחסון של מכשיר ה-Android TV מלא. יש למחוק חלק מהקבצים כדי לפנות שטח."</string>
+ <string name="low_memory" product="tv" msgid="6663680413790323318">"האחסון של מכשיר ה-Android TV מלא. יש למחוק חלק מהקבצים כדי לפנות מקום."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"שטח האחסון של הטלפון מלא. מחק חלק מהקבצים כדי לפנות שטח."</string>
<plurals name="ssl_ca_cert_warning" formatted="false" msgid="2288194355006173029">
<item quantity="two">רשויות אישורים הותקנו</item>
@@ -188,7 +188,7 @@
<item quantity="other">רשויות אישורים הותקנו</item>
<item quantity="one">רשות אישורים הותקנה</item>
</plurals>
- <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"על ידי צד שלישי לא מוכר"</string>
+ <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"על ידי צד שלישי לא ידוע"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"על ידי המנהל של פרופיל העבודה שלך"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"על ידי <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
<string name="work_profile_deleted" msgid="5891181538182009328">"פרופיל העבודה נמחק"</string>
@@ -256,7 +256,7 @@
<string name="global_action_logout" msgid="6093581310002476511">"סיום הפעלה"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"צילום מסך"</string>
<string name="bugreport_title" msgid="8549990811777373050">"דיווח על באג"</string>
- <string name="bugreport_message" msgid="5212529146119624326">"פעולה זו תאסוף מידע על מצב המכשיר הנוכחי שלך על מנת לשלוח אותו כהודעת אימייל. היא תימשך זמן קצר מרגע פתיחת דיווח הבאג ועד לשליחת ההודעה בפועל. אנא המתן בסבלנות."</string>
+ <string name="bugreport_message" msgid="5212529146119624326">"הפעולה הזו תאסוף מידע על מצב המכשיר הנוכחי שלך כדי לשלוח אותו כהודעת אימייל. היא תימשך זמן קצר מרגע פתיחת הדיווח על הבאג ועד לשליחת ההודעה בפועל. יש להמתין בסבלנות."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"דוח אינטראקטיבי"</string>
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"השתמש באפשרות זו ברוב המקרים. היא מאפשרת לך לעקוב אחר התקדמות הדוח, להזין פרטים נוספים על הבעיה וליצור צילומי מסך. היא עשויה להשמיט כמה קטעים שנמצאים פחות בשימוש ואשר יצירת הדיווח עליהם נמשכת זמן רב."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"דוח מלא"</string>
@@ -300,8 +300,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"אפליקציה פועלת"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"אפליקציות שמרוקנות את הסוללה"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"הגדלה"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"מדיניות בנושא אבטחת נגישות"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> משתמשת בסוללה"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> אפליקציות משתמשות בסוללה"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"הקש לקבלת פרטים על צריכה של נתונים וסוללה"</string>
@@ -344,7 +343,7 @@
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"יכול להקיש, להחליק, לעשות תנועת צביטה ולבצע תנועות אחרות."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"תנועות של טביעות אצבעות"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"אפשרות לזהות תנועות בזמן נגיעה בחיישן טביעות האצבע של המכשיר."</string>
- <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"שמירת צילום המסך"</string>
+ <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"צילום המסך"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ניתן לצלם צילום מסך של התצוגה."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"השבת או שנה את שורת המצב"</string>
<string name="permdesc_statusBar" msgid="5809162768651019642">"מאפשר לאפליקציה להשבית את שורת המצב או להוסיף ולהסיר סמלי מערכת."</string>
@@ -365,25 +364,25 @@
<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="permdesc_receiveMms" msgid="958102423732219710">"מאפשרת לאפליקציה לקבל ולעבד הודעות MMS. משמעות הדבר היא שהאפליקציה יכולה לעקוב אחר הודעות שנשלחו למכשיר או למחוק אותן מבלי להציג לך אותן."</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"העברת הודעות של שידור סלולרי"</string>
<string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"מאפשרת לאפליקציה להתחייב למודול של השידור הסלולרי כדי להעביר הודעות של שידור סלולרי כשהן מתקבלות. התראות שידור סלולרי נשלחות במקומות מסוימים כדי להזהיר אותך מפני מצבי חירום. אפליקציות זדוניות עשויות להפריע לביצועים או לפעולה של המכשיר כאשר מתקבל שידור חירום סלולרי."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"ניהול שיחות שנערכות"</string>
<string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"לאפליקציה תהיה אפשרות לראות פרטים על שיחות שנערכות במכשיר ולשלוט בשיחות האלה."</string>
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"קריאת הודעות שידור סלולרי"</string>
<string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"מאפשר לאפליקציה לקרוא הודעות שידור סלולרי שהתקבלו במכשיר שלך. התראות שידור סלולרי נשלחות במקומות מסוימים על מנת להזהיר אותך מפני מצבי חירום. אפליקציות זדוניות עשויות להפריע לביצועים או לפעולה של המכשיר שלך כאשר מתקבל שידור חירום סלולרי."</string>
- <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"קרא עדכוני מנויים"</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="permdesc_readSms" product="tablet" msgid="7912990447198112829">"אפליקציה זו יכולה לקרוא את כל הודעות הטקסט (SMS) המאוחסנות בטאבלט."</string>
- <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"אפליקציה זו יכולה לקרוא את כל הודעות הטקסט (SMS) המאוחסנות במכשיר ה-Android TV."</string>
+ <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"האפליקציה הזו יכולה לקרוא את כל הודעות הטקסט (SMS) המאוחסנות במכשיר ה-Android TV."</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_getTasks" msgid="7460048811831750262">"אחזור אפליקציות פעילות"</string>
- <string name="permdesc_getTasks" msgid="7388138607018233726">"מאפשר לאפליקציה לאחזר מידע לגבי משימות הפועלות כרגע ושפעלו לאחרונה. ייתכן שהדבר יתיר לאפליקציה לגלות מידע לגבי האפליקציות שבהן נעשה שימוש במכשיר."</string>
+ <string name="permdesc_getTasks" msgid="7388138607018233726">"מאפשרת לאפליקציה לאחזר מידע לגבי משימות הפועלות כרגע וכאלו שפעלו לאחרונה. ייתכן שההרשאה הזו תתיר לאפליקציה לגלות מידע לגבי האפליקציות שבהן נעשה שימוש במכשיר."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"ניהול בעלים של פרופיל ומכשיר"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"מאפשרת לאפליקציות להגדיר את הבעלים של הפרופיל ואת בעל המכשיר."</string>
<string name="permlab_reorderTasks" msgid="7598562301992923804">"סידור מחדש של אפליקציות פעילות"</string>
@@ -403,7 +402,7 @@
<string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"מאפשרת לאפליקציה לאחסן חלקים שלה בזיכרון באופן קבוע. פעולה זו עשויה להגביל את הזיכרון הזמין לאפליקציות אחרות ולהאט את הפעולה של מכשיר ה-Android TV."</string>
<string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"מאפשר לאפליקציה להפוך חלקים ממנו לקבועים בזיכרון. פעולה זו עשויה להגביל את הזיכרון הזמין לאפליקציות אחרים ולהאט את פעולת הטלפון."</string>
<string name="permlab_foregroundService" msgid="1768855976818467491">"הרצת שירות קדמה"</string>
- <string name="permdesc_foregroundService" msgid="8720071450020922795">"הרשאה זו מאפשרת לאפליקציה לעשות שימוש בשירותים בקדמה."</string>
+ <string name="permdesc_foregroundService" msgid="8720071450020922795">"ההרשאה הזו מאפשרת לאפליקציה להשתמש בשירותים שפועלים בחזית."</string>
<string name="permlab_getPackageSize" msgid="375391550792886641">"מדידת נפח האחסון של אפליקציות"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"מאפשר לאפליקציה לאחזר את הקוד, הנתונים, וגודלי קובצי המטמון שלו"</string>
<string name="permlab_writeSettings" msgid="8057285063719277394">"שינוי הגדרות מערכת"</string>
@@ -422,7 +421,7 @@
<string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"מאפשרת לאפליקציה לקרוא נתונים על אנשי הקשר השמורים בטלפון שלך. לאפליקציות תהיה גם גישה לחשבונות בטלפון שיצרו אנשי קשר. פעולה זו עשויה לכלול חשבונות שנוצרו על ידי אפליקציות שהתקנת. הרשאה זו מאפשרת לאפליקציות לשמור נתונים של אנשי הקשר שלך, ואפליקציות זדוניות עלולות לשתף נתונים של אנשי קשר ללא ידיעתך."</string>
<string name="permlab_writeContacts" msgid="8919430536404830430">"שינוי אנשי הקשר שלך"</string>
<string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"מאפשרת לאפליקציה לשנות את הנתונים לגבי אנשי הקשר המאוחסנים בטאבלט שלך. הרשאה זו מאפשרת לאפליקציות למחוק נתונים של אנשי קשר."</string>
- <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"מאפשרת לאפליקציה לשנות את הנתונים לגבי אנשי הקשר המאוחסנים במכשיר ה-Android TV שלך. הרשאה זו מאפשרת לאפליקציות למחוק נתונים של אנשי קשר."</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"מאפשרת לאפליקציה לשנות את הנתונים לגבי אנשי הקשר המאוחסנים במכשיר ה-Android TV שלך. ההרשאה הזו מאפשרת לאפליקציות למחוק נתונים של אנשי קשר."</string>
<string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"מאפשרת לאפליקציה לשנות את הנתונים לגבי אנשי הקשר המאוחסנים בטלפון שלך. הרשאה זו מאפשרת לאפליקציות למחוק נתונים של אנשי קשר."</string>
<string name="permlab_readCallLog" msgid="1739990210293505948">"קריאת יומן שיחות"</string>
<string name="permdesc_readCallLog" msgid="8964770895425873433">"אפליקציה זו יכולה לקרוא את היסטוריית השיחות שלך."</string>
@@ -432,10 +431,10 @@
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"מאפשר לאפליקציה לשנות את יומן השיחות של הטלפון, כולל נתונים על שיחות נכנסות ויוצאות. אפליקציות זדוניות עלולות לעשות בכך שימוש כדי למחוק או לשנות את יומן השיחות שלך."</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>
+ <string name="permlab_readCalendar" msgid="6408654259475396200">"קריאה של אירועי יומן והפרטים שלהם"</string>
+ <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"האפליקציה הזו יכולה לקרוא את כל אירועי היומן המאוחסנים בטאבלט, ולשתף או לשמור את נתוני היומן."</string>
<string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"אפליקציה זו יכולה לקרוא את כל אירועי היומן המאוחסנים במכשיר ה-Android TV, ולשתף או לשמור את נתוני היומן."</string>
- <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"אפליקציה זו יכולה לקרוא את כל אירועי היומן המאוחסנים בטלפון, ולשתף או לשמור את נתוני היומן."</string>
+ <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"האפליקציה הזו יכולה לקרוא את כל אירועי היומן המאוחסנים בטלפון, ולשתף או לשמור את נתוני היומן."</string>
<string name="permlab_writeCalendar" msgid="6422137308329578076">"הוספה ושינוי של אירועי יומן ושליחת אימייל לאורחים ללא ידיעת הבעלים"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"אפליקציה זו יכולה להוסיף, להסיר ולשנות אירועי יומן בטאבלט. האפליקציה יכולה לשנות אירועים בלי להודיע לבעליהם ולשלוח הודעות שעשויות להיראות כאילו נשלחו מבעלי יומנים."</string>
<string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"אפליקציה זו יכולה להוסיף, להסיר ולשנות אירועי יומן במכשיר ה-Android TV. האפליקציה יכולה לשלוח הודעות שעשויות להיראות כאילו נשלחו מבעלי יומנים ולשנות אירועים בלי להודיע על כך לבעליהם."</string>
@@ -455,7 +454,7 @@
<string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"הקלטת אודיו ברקע"</string>
<string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"האפליקציה הזו יכולה להשתמש במיקרופון כדי להקליט אודיו בכל זמן."</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"שליחת פקודות אל ה-SIM"</string>
- <string name="permdesc_sim_communication" msgid="4179799296415957960">"מאפשרת ליישום לשלוח פקודות ל-SIM. זוהי הרשאה מסוכנת מאוד."</string>
+ <string name="permdesc_sim_communication" msgid="4179799296415957960">"מאפשרת לאפליקציה לשלוח פקודות ל-SIM. זוהי הרשאה מסוכנת מאוד."</string>
<string name="permlab_activityRecognition" msgid="1782303296053990884">"זיהוי הפעילות הגופנית"</string>
<string name="permdesc_activityRecognition" msgid="8667484762991357519">"האפליקציה מזהה את הפעילות הגופנית שלך."</string>
<string name="permlab_camera" msgid="6320282492904119413">"צלם תמונות וסרטונים"</string>
@@ -474,7 +473,7 @@
<string name="permlab_accessImsCallService" msgid="442192920714863782">"גישה אל שירות שיחות IMS"</string>
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"מאפשר לאפליקציה להשתמש בשירות ה-IMS לביצוע שיחות ללא התערבותך."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"קריאת הסטטוס והזהות של הטלפון"</string>
- <string name="permdesc_readPhoneState" msgid="7229063553502788058">"מאפשר לאפליקציה לגשת לתכונות הטלפון של המכשיר. אישור זה מתיר לאפליקציה לגלות את מספר הטלפון ואת זיהויי המכשיר, האם שיחה פעילה ואת המספר המרוחק המחובר באמצעות שיחה."</string>
+ <string name="permdesc_readPhoneState" msgid="7229063553502788058">"מאפשרת לאפליקציה לגשת לתכונות הטלפון של המכשיר. ההרשאה הזו מתירה לאפליקציה לגלות את מספר הטלפון ואת מזהי המכשיר, אם השיחה פעילה ואת המספר המרוחק המחובר באמצעות שיחה."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ניתוב שיחות דרך המערכת"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"מאפשרת לאפליקציה לנתב את השיחות דרך המערכת כדי לשפר את חוויית השיחה."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ניתן להציג שיחות ולשלוט בהן באמצעות המערכת."</string>
@@ -486,7 +485,7 @@
<string name="permlab_readPhoneNumbers" msgid="5668704794723365628">"גישה למספרי הטלפון"</string>
<string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"מתירה לאפליקציה גישה למספרי הטלפון במכשיר."</string>
<string name="permlab_wakeLock" product="automotive" msgid="1904736682319375676">"מסך המכונית יישאר דלוק"</string>
- <string name="permlab_wakeLock" product="tablet" msgid="1527660973931694000">"מנע מהטאבלט לעבור למצב שינה"</string>
+ <string name="permlab_wakeLock" product="tablet" msgid="1527660973931694000">"מניעה מהטאבלט לעבור למצב שינה"</string>
<string name="permlab_wakeLock" product="tv" msgid="2856941418123343518">"מונעת ממכשיר ה-Android TV להיכנס למצב שינה"</string>
<string name="permlab_wakeLock" product="default" msgid="569409726861695115">"מניעת מעבר הטלפון למצב שינה"</string>
<string name="permdesc_wakeLock" product="automotive" msgid="5995045369683254571">"מסך המכונית יישאר דלוק כשהאפליקציה פועלת."</string>
@@ -501,7 +500,7 @@
<string name="permdesc_setWallpaper" msgid="2973996714129021397">"מאפשר לאפליקציה להגדיר את טפט המערכת."</string>
<string name="permlab_setWallpaperHints" msgid="1153485176642032714">"התאמת גודל הטפט שלך"</string>
<string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"מאפשר לאפליקציה להגדיר את סמני הגודל של טפט המערכת."</string>
- <string name="permlab_setTimeZone" msgid="7922618798611542432">"הגדר אזור זמן"</string>
+ <string name="permlab_setTimeZone" msgid="7922618798611542432">"הגדרת אזור זמן"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"מאפשר לאפליקציה לשנות את אזור הזמן של הטאבלט."</string>
<string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"מאפשרת לאפליקציה לשנות את אזור הזמן של מכשיר ה-Android TV."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"מאפשר לאפליקציה לשנות את אזור הזמן של הטלפון."</string>
@@ -509,8 +508,8 @@
<string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"מאפשר לאפליקציה לקבל רשימה של חשבונות המוכרים לטאבלט. הדבר עשוי לכלול חשבונות שנוצרו על ידי אפליקציות שהתקנת."</string>
<string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"מאפשרת לאפליקציה לקבל את רשימת החשבונות המוכרים למכשיר ה-Android TV. הפרטים עשויים לכלול חשבונות שנוצרו על ידי אפליקציות שהתקנת."</string>
<string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"מאפשר לאפליקציה לקבל רשימה של חשבונות המוכרים לטלפון. הדבר עשוי לכלול חשבונות שנוצרו על ידי אפליקציות שהתקנת."</string>
- <string name="permlab_accessNetworkState" msgid="2349126720783633918">"הצג חיבורי רשת"</string>
- <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"מאפשר לאפליקציה להציג מידע לגבי חיבורי רשת, למשל, אילו רשתות קיימות ומחוברות."</string>
+ <string name="permlab_accessNetworkState" msgid="2349126720783633918">"הצגת חיבורי רשת"</string>
+ <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"מאפשרת לאפליקציה להציג מידע לגבי חיבורי רשת, למשל, אילו רשתות קיימות ומחוברות."</string>
<string name="permlab_createNetworkSockets" msgid="3224420491603590541">"קבלת גישת רשת מלאה"</string>
<string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"מאפשר לאפליקציה ליצור Sockets ולהשתמש בפרוטוקולי רשת מותאמים אישית. הדפדפן, כמו אפליקציות אחרות, מספק אמצעים לשליחת נתונים לאינטרנט, כך שאישור זה אינו נחוץ לשליחת נתונים לאינטרנט."</string>
<string name="permlab_changeNetworkState" msgid="8945711637530425586">"שנה את קישוריות הרשת"</string>
@@ -551,7 +550,7 @@
<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="permlab_useFingerprint" msgid="1001421069766751922">"שימוש בחומרה של טביעות אצבעות"</string>
<string name="permdesc_useFingerprint" msgid="412463055059323742">"מאפשר לאפליקציה להשתמש בחומרה של טביעות אצבעות לצורך אימות"</string>
<string name="permlab_audioWrite" msgid="8501705294265669405">"לשנות את אוסף המוזיקה שלך"</string>
<string name="permdesc_audioWrite" msgid="8057399517013412431">"מאפשרת לאפליקציה לשנות את אוסף המוזיקה שלך."</string>
@@ -570,7 +569,7 @@
<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_device_not_secured" msgid="3129845065043995924">"עוד לא הוגדרו קוד גישה, קו ביטול נעילה או סיסמה"</string>
+ <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"עוד לא הוגדרו קוד אימות, קו ביטול נעילה או סיסמה"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"שגיאה באימות"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"שימוש בנעילת מסך"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="8638638125397857315">"יש להזין את פרטי הכניסה של המכשיר כדי להמשיך"</string>
@@ -586,10 +585,10 @@
<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>
+ <string name="fingerprint_error_timeout" msgid="2946635815726054226">"נגמר הזמן הקצוב לטביעת אצבע. אפשר לנסות שוב."</string>
<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_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_unable_to_process" msgid="1148553603490048742">"כדאי לנסות שוב."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"לא נרשמו טביעות אצבע."</string>
@@ -607,7 +606,7 @@
<string name="permdesc_manageFace" msgid="6204569688492710471">"מאפשרת לאפליקציה להפעיל שיטות להוספה ומחיקה של תבניות פנים שבהן ייעשה שימוש."</string>
<string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"שימוש בחומרה לשחרור נעילה על ידי זיהוי פנים"</string>
<string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"מאפשרת לאפליקציה להשתמש בחומרה לשחרור נעילה על ידי זיהוי פנים לצורך אימות"</string>
- <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"שחרור נעילה בזיהוי פנים"</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>
<string name="face_acquired_insufficient" msgid="2150805835949162453">"לא ניתן היה לקלוט את הפנים במדויק. יש לנסות שוב."</string>
@@ -615,7 +614,7 @@
<string name="face_acquired_too_dark" msgid="252573548464426546">"התמונה חשוכה מדי. צריך תאורה חזקה יותר."</string>
<string name="face_acquired_too_close" msgid="1628767882971469833">"יש להרחיק את הטלפון."</string>
<string name="face_acquired_too_far" msgid="5098567726427173896">"צריך לקרב את הטלפון."</string>
- <string name="face_acquired_too_high" msgid="4868033653626081839">"צריך להגביה את הטלפון."</string>
+ <string name="face_acquired_too_high" msgid="4868033653626081839">"צריך להרים את הטלפון גבוה יותר."</string>
<string name="face_acquired_too_low" msgid="1512237819632165945">"צריך להוריד את הטלפון."</string>
<string name="face_acquired_too_right" msgid="2513391513020932655">"צריך להזיז את הטלפון שמאלה."</string>
<string name="face_acquired_too_left" msgid="8882499346502714350">"צריך להזיז את הטלפון ימינה."</string>
@@ -655,7 +654,7 @@
<string name="permdesc_readSyncSettings" msgid="1325658466358779298">"מאפשר לאפליקציה לקרוא את הגדרות הסנכרון של חשבון. לדוגמה, ניתן לגלות כך האם האפליקציה \'אנשים\' מסונכרן עם חשבון כלשהו."</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"הפעלת וכיבוי סנכרון"</string>
<string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"מאפשר לאפליקציה לשנות את הגדרות הסנכרון של חשבון. לדוגמה, ניתן להשתמש בכך על מנת להפעיל סנכרון של האפליקציה \'אנשים\' עם חשבון כלשהו."</string>
- <string name="permlab_readSyncStats" msgid="3747407238320105332">"קרא את הנתונים הסטטיסטיים של הסינכרון"</string>
+ <string name="permlab_readSyncStats" msgid="3747407238320105332">"קריאת הנתונים הסטטיסטיים של הסנכרון"</string>
<string name="permdesc_readSyncStats" msgid="3867809926567379434">"מאפשר לאפליקציה לקרוא את סטטיסטיקת הסנכרון של חשבון, כולל היסטוריית אירועי הסנכרון וכמות הנתונים שסונכרנה."</string>
<string name="permlab_sdcardRead" msgid="5791467020950064920">"קריאת התוכן של האחסון המשותף שלך"</string>
<string name="permdesc_sdcardRead" msgid="6872973242228240382">"מאפשר לאפליקציה לקרוא את התוכן של האחסון המשותף."</string>
@@ -663,7 +662,7 @@
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"מאפשר לאפליקציה לכתוב את התוכן של האחסון המשותף."</string>
<string name="permlab_use_sip" msgid="8250774565189337477">"ביצוע/קבלה של שיחות SIP"</string>
<string name="permdesc_use_sip" msgid="3590270893253204451">"אפשר לאפליקציה לבצע ולקבל שיחות SIP."</string>
- <string name="permlab_register_sim_subscription" msgid="1653054249287576161">"רשום חיבורי Telecom SIM חדשים"</string>
+ <string name="permlab_register_sim_subscription" msgid="1653054249287576161">"רישום חיבורי Telecom SIM חדשים"</string>
<string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"מאפשר לאפליקציה לרשום חיבורי Telecom SIM חדשים."</string>
<string name="permlab_register_call_provider" msgid="6135073566140050702">"רשום חיבורי Telecom חדשים"</string>
<string name="permdesc_register_call_provider" msgid="4201429251459068613">"מאפשר לאפליקציה לרשום חיבורי תקשורת חדשים."</string>
@@ -684,9 +683,9 @@
<string name="permlab_accessNotifications" msgid="7130360248191984741">"גישה להתראות"</string>
<string name="permdesc_accessNotifications" msgid="761730149268789668">"מאפשר לאפליקציה לאחזר, לבדוק ולמחוק התראות, כולל כאלה שפורסמו על ידי אפליקציות אחרות."</string>
<string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"איגוד לשירות של מאזין להתראות"</string>
- <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"הרשאה זו מאפשרת למשתמש לבצע איגוד לממשק הרמה העליונה של שירות מאזין להתראות. הרשאה זו אף פעם אינה נחוצה לאפליקציות רגילים."</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"מאפשרת למשתמש לבצע איגוד לממשק הרמה העליונה של שירות האזנה להתראות. ההרשאה הזו אינה נחוצה לאפליקציות רגילות."</string>
<string name="permlab_bindConditionProviderService" msgid="5245421224814878483">"איגוד לשירות ספק תנאי"</string>
- <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"מאפשרת לבעלים לאגד לממשק ברמה העליונה של שירות ספק תנאי. לעולם לא אמורה להיות נחוצה עבור אפליקציות רגילות."</string>
+ <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"מאפשרת לבעלים לאגד לממשק ברמה העליונה של שירות ספק תנאי. לא אמורה להיות נחוצה עבור אפליקציות רגילות."</string>
<string name="permlab_bindDreamService" msgid="4776175992848982706">"איגוד לשירות ׳חלום בהקיץ׳"</string>
<string name="permdesc_bindDreamService" msgid="9129615743300572973">"מאפשרת לבעלים לבצע איגוד לממשק הרמה העליונה של שירות ׳חלום בהקיץ׳. הרשאה זו אף פעם אינה נחוצה לאפליקציות רגילות."</string>
<string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"הפעלה של אפליקציית תצורה שסופקה על ידי ספק"</string>
@@ -704,7 +703,7 @@
<string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"איגוד לשירות העברת הודעות של ספק"</string>
<string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"מאפשרת לבעלים לאגד לממשק ברמה העליונה של שירות העברת הודעות של ספק. לעולם לא אמורה להיות נחוצה עבור אפליקציות רגילות."</string>
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"איגוד לשירותי ספק"</string>
- <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"מאפשר לבעלים לאגד לשירות ספק. לעולם לא אמור להיות נחוץ לאפליקציות רגילות."</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="permlab_startViewPermissionUsage" msgid="1504564328641112341">"התחלת צפייה בהרשאות השימוש"</string>
@@ -712,7 +711,7 @@
<string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"גישה לנתוני חיישנים בתדירות דגימה גבוהה"</string>
<string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"האפליקציה תוכל לדגום נתוני חיישנים בתדירות של מעל 200 הרץ"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"הגדר כללי סיסמה"</string>
- <string name="policydesc_limitPassword" msgid="4105491021115793793">"קביעת האורך הנדרש והתווים המותרים בסיסמאות ובקודי הגישה של מסך הנעילה."</string>
+ <string name="policydesc_limitPassword" msgid="4105491021115793793">"קביעת האורך הנדרש והתווים המותרים בסיסמאות ובקודי האימות של מסך הנעילה."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"מעקב אחר ניסיונות לביטול של נעילת המסך"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ניהול מעקב אחר מספר הסיסמאות השגויות שמוקלדות בעת ביטול נעילת המסך, וביצוע נעילה של הטאבלט, או מחיקה של כל נתוני הטאבלט, אם מוקלדות יותר מדי סיסמאות שגויות."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"מעקב אחר מספר הסיסמאות השגויות שהוזנו בעת ביטול נעילת המסך, כמו גם נעילה של מכשיר ה-Android TV או מחיקה של כל נתוני מכשיר ה-Android TV אם הוזנו יותר מדי סיסמאות שגויות."</string>
@@ -725,7 +724,7 @@
<string name="policylab_forceLock" msgid="7360335502968476434">"נעילת המסך"</string>
<string name="policydesc_forceLock" msgid="1008844760853899693">"שליטה באופן ובתזמון של נעילת המסך."</string>
<string name="policylab_wipeData" msgid="1359485247727537311">"מחיקת כל הנתונים"</string>
- <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"מחק את נתוני הטאבלט ללא אזהרה על ידי ביצוע איפוס נתוני יצרן."</string>
+ <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"מחיקה של נתוני הטאבלט ללא אזהרה, באמצעות איפוס לנתוני היצרן."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"מחיקה ללא אזהרה של נתוני מכשיר ה-Android TV באמצעות איפוס לנתוני היצרן."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"מחיקה של נתוני הטלפון, ללא אזהרה, על ידי ביצוע איפוס נתוני יצרן."</string>
<string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"מחיקת נתוני משתמש"</string>
@@ -738,10 +737,10 @@
<string name="policydesc_expirePassword" msgid="9136524319325960675">"שינוי התדירות לדרישת השינוי של הסיסמה, קוד הגישה או קו ביטול הנעילה של מסך הנעילה."</string>
<string name="policylab_encryptedStorage" msgid="9012936958126670110">"הגדר הצפנת אחסון"</string>
<string name="policydesc_encryptedStorage" msgid="1102516950740375617">"דרוש שנתוני אפליקציות מאוחסנות יהיו מוצפנים."</string>
- <string name="policylab_disableCamera" msgid="5749486347810162018">"השבת מצלמות"</string>
+ <string name="policylab_disableCamera" msgid="5749486347810162018">"השבתת מצלמות"</string>
<string name="policydesc_disableCamera" msgid="3204405908799676104">"מנע שימוש בכל המצלמות שבמכשיר."</string>
<string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"השבת חלק מהתכונות של נעילת המסך"</string>
- <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"מנע שימוש בחלק מהתכונות של נעילת המסך."</string>
+ <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"מניעת שימוש בחלק מהתכונות של נעילת המסך."</string>
<string-array name="phoneTypes">
<item msgid="8996339953292723951">"בית"</item>
<item msgid="7740243458912727194">"נייד"</item>
@@ -844,7 +843,7 @@
<string name="relationTypeFather" msgid="3856225062864790596">"אב"</string>
<string name="relationTypeFriend" msgid="3192092625893980574">"חבר"</string>
<string name="relationTypeManager" msgid="2272860813153171857">"מנהל"</string>
- <string name="relationTypeMother" msgid="2331762740982699460">"אם"</string>
+ <string name="relationTypeMother" msgid="2331762740982699460">"אמא"</string>
<string name="relationTypeParent" msgid="4177920938333039882">"הורה"</string>
<string name="relationTypePartner" msgid="4018017075116766194">"שותף"</string>
<string name="relationTypeReferredBy" msgid="5285082289602849400">"הופנה על ידי"</string>
@@ -856,14 +855,14 @@
<string name="sipAddressTypeWork" msgid="7873967986701216770">"עבודה"</string>
<string name="sipAddressTypeOther" msgid="6317012577345187275">"אחר"</string>
<string name="quick_contacts_not_available" msgid="1262709196045052223">"לא נמצאה אפליקציה להצגת התוכן הזה."</string>
- <string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"הקלד קוד גישה"</string>
- <string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"הקלד את קוד ה-PUK וקוד הגישה החדש"</string>
+ <string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"יש להקליד קוד אימות"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"יש להקליד את קוד ה-PUK ואת קוד האימות החדש"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"קוד PUK"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"קוד גישה חדש"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"קוד אימות חדש"</string>
<string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"הקש כדי להקליד את הסיסמה"</font></string>
<string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"הקלד סיסמה לביטול הנעילה"</string>
- <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"הקלד קוד גישה לביטול הנעילה"</string>
- <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"קוד גישה שגוי"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"יש להקליד קוד אימות לביטול הנעילה"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"קוד אימות שגוי"</string>
<string name="keyguard_label_text" msgid="3841953694564168384">"כדי לבטל את הנעילה, לחץ על \'תפריט\' ולאחר מכן על 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"מספר חירום"</string>
<string name="lockscreen_carrier_default" msgid="6192313772955399160">"אין שירות"</string>
@@ -887,7 +886,7 @@
<string name="lockscreen_permanent_disabled_sim_message_short" msgid="3812893366715730539">"לא ניתן להשתמש בכרטיס SIM זה."</string>
<string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"כרטיס ה-SIM שלך הושבת לצמיתות.\nפנה לספק השירות האלחוטי שלך לקבלת כרטיס SIM אחר."</string>
<string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"הרצועה הקודמת"</string>
- <string name="lockscreen_transport_next_description" msgid="2931509904881099919">"הרצועה הבאה"</string>
+ <string name="lockscreen_transport_next_description" msgid="2931509904881099919">"הטראק הבא"</string>
<string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"השהה"</string>
<string name="lockscreen_transport_play_description" msgid="106868788691652733">"הפעלה"</string>
<string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"הפסק"</string>
@@ -912,10 +911,10 @@
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"ניסית לבטל בצורה שגויה את הנעילה של מכשיר ה-Android TV <xliff:g id="NUMBER">%d</xliff:g> פעמים. מכשיר ה-Android TV יעבור כעת איפוס לברירת המחדל של היצרן."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"ביצעת <xliff:g id="NUMBER">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. הטלפון יעבור כעת איפוס לברירת המחדל של היצרן."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"נסה שוב בעוד <xliff:g id="NUMBER">%d</xliff:g> שניות."</string>
- <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"שכחת את הקו?"</string>
+ <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"שכחת את קו ביטול הנעילה?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"ביטול נעילת חשבון"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"בוצעו ניסיונות רבים מדי לשרטוט קו ביטול נעילה."</string>
- <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"כדי לבטל את הנעילה, היכנס באמצעות חשבון Google שלך."</string>
+ <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"כדי לבטל את הנעילה, עליך להיכנס באמצעות חשבון Google שלך."</string>
<string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"שם משתמש (אימייל)"</string>
<string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"סיסמה"</string>
<string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"כניסה"</string>
@@ -949,7 +948,7 @@
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"ביטול נעילה על ידי שרטוט קו."</string>
<string name="keyguard_accessibility_face_unlock" msgid="632407612842329815">"ביטול נעילה באמצעות זיהוי פנים."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"ביטול נעילה באמצעות קוד גישה."</string>
- <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"ביטול נעילה של קוד הגישה ל-SIM."</string>
+ <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"ביטול הנעילה של קוד האימות ל-SIM."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"ביטול נעילה של PUK ל-SIM."</string>
<string name="keyguard_accessibility_password_unlock" msgid="6130186108581153265">"ביטול נעילה באמצעות סיסמה."</string>
<string name="keyguard_accessibility_pattern_area" msgid="1419570880512350689">"אזור לשרטוט קו ביטול נעילה."</string>
@@ -1005,7 +1004,7 @@
<string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"שינוי הרשאות המיקום הגיאוגרפי של הדפדפן"</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_notnow" msgid="2878327088951240061">"לא עכשיו"</string>
<string name="save_password_remember" msgid="6490888932657708341">"זכור"</string>
<string name="save_password_never" msgid="6776808375903410659">"אף פעם"</string>
<string name="open_permission_deny" msgid="5136793905306987251">"אין לך הרשאה לפתוח דף זה."</string>
@@ -1028,10 +1027,10 @@
<string name="search_hint" msgid="455364685740251925">"חיפוש…"</string>
<string name="searchview_description_search" msgid="1045552007537359343">"חיפוש"</string>
<string name="searchview_description_query" msgid="7430242366971716338">"שאילתת חיפוש"</string>
- <string name="searchview_description_clear" msgid="1989371719192982900">"נקה שאילתה"</string>
- <string name="searchview_description_submit" msgid="6771060386117334686">"שלח שאילתה"</string>
+ <string name="searchview_description_clear" msgid="1989371719192982900">"ניקוי שאילתה"</string>
+ <string name="searchview_description_submit" msgid="6771060386117334686">"שליחת שאילתה"</string>
<string name="searchview_description_voice" msgid="42360159504884679">"חיפוש קולי"</string>
- <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"האם להפעיל את התכונה \'חקור על ידי מגע\'?"</string>
+ <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"האם להפעיל את התכונה \'גילוי באמצעות מגע\'?"</string>
<string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> רוצה להפעיל את התכונה \'חקור על ידי מגע\'. כאשר התכונה \'חקור על ידי מגע\' מופעלת, אתה יכול לשמוע או לראות תיאורים של הפריטים שעליהם אצבעך מונחת או לקיים אינטראקציה עם הטאבלט באמצעות תנועות אצבע."</string>
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> רוצה להפעיל את התכונה \'חקור על ידי מגע\'. כאשר התכונה \'חקור על ידי מגע\' מופעלת, אתה יכול לשמוע או לראות תיאורים של הפריטים שעליהם אצבעך מונחת או לקיים אינטראקציה עם הטלפון באמצעות תנועות אצבע."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"לפני חודש אחד"</string>
@@ -1168,8 +1167,8 @@
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="1532369154488982046">"בחר הכל"</string>
- <string name="cut" msgid="2561199725874745819">"חתוך"</string>
- <string name="copy" msgid="5472512047143665218">"העתק"</string>
+ <string name="cut" msgid="2561199725874745819">"חיתוך"</string>
+ <string name="copy" msgid="5472512047143665218">"העתקה"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"ההעתקה אל הלוח נכשלה"</string>
<string name="paste" msgid="461843306215520225">"הדבק"</string>
<string name="paste_as_plain_text" msgid="7664800665823182587">"הדבק כטקסט פשוט"</string>
@@ -1185,7 +1184,7 @@
<string name="deleteText" msgid="4200807474529938112">"מחיקה"</string>
<string name="inputMethod" msgid="1784759500516314751">"שיטת קלט"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"פעולות טקסט"</string>
- <string name="low_internal_storage_view_title" msgid="9024241779284783414">"שטח האחסון אוזל"</string>
+ <string name="low_internal_storage_view_title" msgid="9024241779284783414">"מקום האחסון עומד להיגמר"</string>
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"אין מספיק שטח אחסון עבור המערכת. ודא שיש לך שטח פנוי בגודל 250MB התחל שוב."</string>
<string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל"</string>
@@ -1214,10 +1213,10 @@
<string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"פתיחת קישורים של <xliff:g id="HOST">%1$s</xliff:g> באמצעות <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
<string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"הענקת גישה"</string>
<string name="whichEditApplication" msgid="6191568491456092812">"ערוך באמצעות"</string>
- <string name="whichEditApplicationNamed" msgid="8096494987978521514">"ערוך באמצעות %1$s"</string>
+ <string name="whichEditApplicationNamed" msgid="8096494987978521514">"עריכה באמצעות %1$s"</string>
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"עריכה"</string>
<string name="whichSendApplication" msgid="4143847974460792029">"שיתוף"</string>
- <string name="whichSendApplicationNamed" msgid="4470386782693183461">"שתף באמצעות %1$s"</string>
+ <string name="whichSendApplicationNamed" msgid="4470386782693183461">"שיתוף באמצעות %1$s"</string>
<string name="whichSendApplicationLabel" msgid="7467813004769188515">"שתף"</string>
<string name="whichSendToApplication" msgid="77101541959464018">"שליחה באמצעות"</string>
<string name="whichSendToApplicationNamed" msgid="3385686512014670003">"שליחה באמצעות %1$s"</string>
@@ -1233,7 +1232,7 @@
<string name="clearDefaultHintMsg" msgid="1325866337702524936">"נקה את הגדרת המחדל ב\'הגדרות מערכת\' < Google Apps < \'הורדות\'."</string>
<string name="chooseActivity" msgid="8563390197659779956">"בחירת פעולה"</string>
<string name="chooseUsbActivity" msgid="2096269989990986612">"בחר אפליקציה עבור התקן ה-USB"</string>
- <string name="noApplications" msgid="1186909265235544019">"אין אפליקציות שיכולות לבצע פעולה זו."</string>
+ <string name="noApplications" msgid="1186909265235544019">"אין אפליקציות שיכולות לבצע את הפעולה הזו."</string>
<string name="aerr_application" msgid="4090916809370389109">"האפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> הפסיקה"</string>
<string name="aerr_process" msgid="4268018696970966407">"התהליך <xliff:g id="PROCESS">%1$s</xliff:g> הפסיק"</string>
<string name="aerr_application_repeated" msgid="7804378743218496566">"האפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> נעצרת שוב ושוב"</string>
@@ -1255,9 +1254,9 @@
<string name="webpage_unresponsive" msgid="7850879412195273433">"הדף אינו מגיב.\n\nהאם אתה רוצה לסגור אותו?"</string>
<string name="launch_warning_title" msgid="6725456009564953595">"הפנייה מחדש של אפליקציה"</string>
<string name="launch_warning_replace" msgid="3073392976283203402">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל כעת."</string>
- <string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g> הופעל במקור."</string>
+ <string name="launch_warning_original" msgid="3332206576800169626">"אפליקציית <xliff:g id="APP_NAME">%1$s</xliff:g> הופעלה במקור."</string>
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"שינוי קנה-מידה"</string>
- <string name="screen_compat_mode_show" msgid="5080361367584709857">"הצג תמיד"</string>
+ <string name="screen_compat_mode_show" msgid="5080361367584709857">"להציג תמיד"</string>
<string name="screen_compat_mode_hint" msgid="4032272159093750908">"אפשר תכונה זו מחדש ב\'הגדרות מערכת\' < Google Apps < \'הורדות\'."</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> אינו תומך בהגדרת הגודל הנוכחית של התצוגה, והתנהגותו עשויה להיות בלתי צפויה."</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"הצג תמיד"</string>
@@ -1286,8 +1285,8 @@
<string name="heavy_weight_switcher_text" msgid="6814316627367160126">"לקבלת ביצועים טובים יותר, רק אחד מבין המשחקים האלה יכול להיות פתוח בכל פעם."</string>
<string name="old_app_action" msgid="725331621042848590">"חזרה אל <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
<string name="new_app_action" msgid="547772182913269801">"פתיחת <xliff:g id="NEW_APP">%1$s</xliff:g>"</string>
- <string name="new_app_description" msgid="1958903080400806644">"<xliff:g id="OLD_APP">%1$s</xliff:g> האפליקציה תיסגר ללא שמירה"</string>
- <string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> חורג מהגבלת הזיכרון"</string>
+ <string name="new_app_description" msgid="1958903080400806644">"אפליקציית <xliff:g id="OLD_APP">%1$s</xliff:g> תיסגר ללא שמירה"</string>
+ <string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> חורג ממגבלת הזיכרון"</string>
<string name="dump_heap_ready_notification" msgid="2302452262927390268">"Dump של ערימה בשביל <xliff:g id="PROC">%1$s</xliff:g> מוכן"</string>
<string name="dump_heap_notification_detail" msgid="8431586843001054050">"Dump של ערימה נאסף. יש להקיש כדי לשתף."</string>
<string name="dump_heap_title" msgid="4367128917229233901">"האם לשתף את נתוני ה-Dump של הערימה?"</string>
@@ -1304,7 +1303,7 @@
<string name="volume_alarm" msgid="4486241060751798448">"עוצמת קול של התראה"</string>
<string name="volume_notification" msgid="6864412249031660057">"עוצמת הקול של ההתראות"</string>
<string name="volume_unknown" msgid="4041914008166576293">"עוצמת קול"</string>
- <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"עוצמת קול של Bluetooth"</string>
+ <string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"עוצמת הקול של Bluetooth"</string>
<string name="volume_icon_description_ringer" msgid="2187800636867423459">"עוצמת קול של רינגטון"</string>
<string name="volume_icon_description_incall" msgid="4491255105381227919">"עוצמת קול של שיחות"</string>
<string name="volume_icon_description_media" msgid="4997633254078171233">"עוצמת קול של מדיה"</string>
@@ -1393,7 +1392,7 @@
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"יש להקיש כדי להשבית ניפוי באגים אלחוטי"</string>
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"יש לבחור כדי להשבית ניפוי באגים אלחוטי."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"מצב מסגרת בדיקה הופעל"</string>
- <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"כדי להשבית את מצב מסגרת בדיקה צריך לאפס להגדרות היצרן."</string>
+ <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"כדי להשבית את מצב \'מסגרת בדיקה\' צריך לאפס להגדרות היצרן."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"קונסולה סדרתית מופעלת"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"קיימת השפעה על הביצועים. כדי להשבית, יש לבדוק את תוכנת האתחול."</string>
<string name="usb_contaminant_detected_title" msgid="4359048603069159678">"יש נוזלים או חלקיקים ביציאת ה-USB"</string>
@@ -1487,10 +1486,10 @@
<string name="ime_action_done" msgid="6299921014822891569">"סיום"</string>
<string name="ime_action_previous" msgid="6548799326860401611">"הקודם"</string>
<string name="ime_action_default" msgid="8265027027659800121">"בצע"</string>
- <string name="dial_number_using" msgid="6060769078933953531">"חייג למספר\nבאמצעות <xliff:g id="NUMBER">%s</xliff:g>"</string>
+ <string name="dial_number_using" msgid="6060769078933953531">"חיוג למספר\nבאמצעות <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="6200708808003692594">"צור איש קשר\nבאמצעות <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"האפליקציות הבאות מבקשות אישור לגשת לחשבונך, כעת ובעתיד."</string>
- <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"האם ברצונך לאפשר בקשה זו?"</string>
+ <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"לאפשר את הבקשה הזו?"</string>
<string name="grant_permissions_header_text" msgid="3420736827804657201">"בקשת גישה"</string>
<string name="allow" msgid="6195617008611933762">"כן, זה בסדר"</string>
<string name="deny" msgid="6632259981847676572">"עדיף שלא"</string>
@@ -1539,10 +1538,10 @@
<string name="find" msgid="5015737188624767706">"מצא"</string>
<string name="websearch" msgid="5624340204512793290">"חיפוש באינטרנט"</string>
<string name="find_next" msgid="5341217051549648153">"חפש את הבא"</string>
- <string name="find_previous" msgid="4405898398141275532">"חפש את הקודם"</string>
+ <string name="find_previous" msgid="4405898398141275532">"חיפוש של הקודם"</string>
<string name="gpsNotifTicker" msgid="3207361857637620780">"בקשת מיקום מאת <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="gpsNotifTitle" msgid="1590033371665669570">"בקשת מיקום"</string>
- <string name="gpsNotifMessage" msgid="7346649122793758032">"מבוקש על ידי <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
+ <string name="gpsNotifMessage" msgid="7346649122793758032">"הבקשה נשלחה על ידי <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="3719843080744112940">"כן"</string>
<string name="gpsVerifNo" msgid="1671201856091564741">"לא"</string>
<string name="sync_too_many_deletes" msgid="6999440774578705300">"חרגת ממגבלת המחיקה"</string>
@@ -1556,7 +1555,7 @@
<string name="number_picker_increment_button" msgid="7621013714795186298">"הוסף"</string>
<string name="number_picker_decrement_button" msgid="5116948444762708204">"הפחת"</string>
<string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> לחיצה ארוכה."</string>
- <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"הסט למעלה כדי להוסיף ולמטה כדי להפחית."</string>
+ <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"צריך להסיט למעלה כדי להוסיף ולמטה כדי להפחית."</string>
<string name="time_picker_increment_minute_button" msgid="7195870222945784300">"הוספת דקה"</string>
<string name="time_picker_decrement_minute_button" msgid="230925389943411490">"הפחת דקה"</string>
<string name="time_picker_increment_hour_button" msgid="3063572723197178242">"הוסף שעה"</string>
@@ -1564,7 +1563,7 @@
<string name="time_picker_increment_set_pm_button" msgid="5889149366900376419">"הגדר PM"</string>
<string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"הגדר AM"</string>
<string name="date_picker_increment_month_button" msgid="3447263316096060309">"הוסף חודש"</string>
- <string name="date_picker_decrement_month_button" msgid="6531888937036883014">"הפחת חודש"</string>
+ <string name="date_picker_decrement_month_button" msgid="6531888937036883014">"הפחתת חודש"</string>
<string name="date_picker_increment_day_button" msgid="4349336637188534259">"הוסף יום"</string>
<string name="date_picker_decrement_day_button" msgid="6840253837656637248">"הפחת יום"</string>
<string name="date_picker_increment_year_button" msgid="7608128783435372594">"הוסף שנה"</string>
@@ -1582,10 +1581,10 @@
<string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"לא ניתן היה להפעיל את <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="shareactionprovider_share_with" msgid="2753089758467748982">"שתף עם"</string>
<string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"שתף עם <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
- <string name="content_description_sliding_handle" msgid="982510275422590757">"ידית להחלקה. לחיצה ארוכה."</string>
+ <string name="content_description_sliding_handle" msgid="982510275422590757">"נקודת אחיזה להחלקה. לחיצה ארוכה."</string>
<string name="description_target_unlock_tablet" msgid="7431571180065859551">"החלק לביטול נעילה."</string>
<string name="action_bar_home_description" msgid="1501655419158631974">"נווט לדף הבית"</string>
- <string name="action_bar_up_description" msgid="6611579697195026932">"נווט למעלה"</string>
+ <string name="action_bar_up_description" msgid="6611579697195026932">"ניווט למעלה"</string>
<string name="action_menu_overflow_description" msgid="4579536843510088170">"אפשרויות נוספות"</string>
<string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string>
@@ -1601,7 +1600,7 @@
<string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"הגעת למגבלה של חבילת הגלישה"</string>
<string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"הגעת למגבלת נתוני Wi-Fi"</string>
<string name="data_usage_limit_body" msgid="3567699582000085710">"הנתונים הושהו להמשך המחזור"</string>
- <string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"חריגה מהמגבלה של חבילת הגלישה"</string>
+ <string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"חרגת מהמגבלה של חבילת הגלישה"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"חריגה ממגבלת נתוני ה-Wi-Fi"</string>
<string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"חרגת ב-<xliff:g id="SIZE">%s</xliff:g> מעבר למגבלה המוגדרת"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"נתוני הרקע מוגבלים"</string>
@@ -1624,7 +1623,7 @@
<string name="sha256_fingerprint" msgid="7103976380961964600">"טביעת אצבע SHA-256:"</string>
<string name="sha1_fingerprint" msgid="2339915142825390774">"טביעת אצבע SHA-1:"</string>
<string name="activity_chooser_view_see_all" msgid="3917045206812726099">"הצג הכל"</string>
- <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"בחר פעילות"</string>
+ <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"בחירת פעילות"</string>
<string name="share_action_provider_share_with" msgid="1904096863622941880">"שתף עם"</string>
<string name="sending" msgid="206925243621664438">"שולח…"</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"להפעיל את הדפדפן?"</string>
@@ -1682,7 +1681,7 @@
<string name="kg_invalid_puk" msgid="4809502818518963344">"הזן מחדש את קוד PUK הנכון. ניסיונות חוזרים ישביתו לצמיתות את כרטיס ה-SIM."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"קודי הגישה אינם תואמים"</string>
<string name="kg_login_too_many_attempts" msgid="699292728290654121">"ניסיונות רבים מדי לשרטוט קו ביטול נעילה."</string>
- <string name="kg_login_instructions" msgid="3619844310339066827">"כדי לבטל את הנעילה, היכנס באמצעות חשבון Google שלך."</string>
+ <string name="kg_login_instructions" msgid="3619844310339066827">"כדי לבטל את הנעילה, עליך להיכנס באמצעות חשבון Google שלך."</string>
<string name="kg_login_username_hint" msgid="1765453775467133251">"שם משתמש (אימייל)"</string>
<string name="kg_login_password_hint" msgid="3330530727273164402">"סיסמה"</string>
<string name="kg_login_submit_button" msgid="893611277617096870">"כניסה"</string>
@@ -1699,7 +1698,7 @@
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"ניסית לבטל בצורה שגויה את הנעילה של מכשיר ה-Android TV <xliff:g id="NUMBER">%d</xliff:g> פעמים. מכשיר ה-Android TV יעבור כעת איפוס לברירת המחדל של היצרן."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"ביצעת <xliff:g id="NUMBER">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. הטלפון יעבור כעת איפוס לברירת המחדל של היצרן."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטאבלט באמצעות חשבון אימייל.\n\nנסה שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"שרטטת קו ביטול נעילה שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את הנעילה של מכשיר ה-Android TV באמצעות חשבון אימייל.\n\n נסה שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"שרטטת קו ביטול נעילה שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, יהיה צורך לבטל את הנעילה של מכשיר ה-Android TV באמצעות חשבון אימייל.\n\n יש לנסות שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלפון באמצעות חשבון אימייל.\n\nנסה שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"הסרה"</string>
@@ -1731,10 +1730,10 @@
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"עריכת קיצורי הדרך"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"סיום"</string>
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"כבה את קיצור הדרך"</string>
- <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"השתמש בקיצור הדרך"</string>
+ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"שימוש בקיצור הדרך"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"היפוך צבעים"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"תיקון צבעים"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"הפחתה של עוצמת הבהירות"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"מעומעם במיוחד"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"יש ללחוץ לחיצה ארוכה על שני לחצני עוצמת הקול למשך שלוש שניות כדי להשתמש בשירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1751,7 +1750,7 @@
<string name="owner_name" msgid="8713560351570795743">"בעלים"</string>
<string name="error_message_title" msgid="4082495589294631966">"שגיאה"</string>
<string name="error_message_change_not_allowed" msgid="843159705042381454">"מנהל המערכת שלך אינו מתיר שינוי זה"</string>
- <string name="app_not_found" msgid="3429506115332341800">"לא נמצאה אפליקציה שתומכת בפעולה זו"</string>
+ <string name="app_not_found" msgid="3429506115332341800">"לא נמצאה אפליקציה שתומכת בפעולה הזו"</string>
<string name="revoke" msgid="5526857743819590458">"ביטול"</string>
<string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string>
<string name="mediasize_iso_a1" msgid="4063589931031977223">"ISO A1"</string>
@@ -1841,15 +1840,15 @@
<string name="reason_unknown" msgid="5599739807581133337">"לא ידוע"</string>
<string name="reason_service_unavailable" msgid="5288405248063804713">"שירות ההדפסה לא הופעל"</string>
<string name="print_service_installed_title" msgid="6134880817336942482">"שירות <xliff:g id="NAME">%s</xliff:g> מותקן"</string>
- <string name="print_service_installed_message" msgid="7005672469916968131">"הקש כדי להפעיל"</string>
- <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"הזן את קוד הגישה של מנהל המכשיר"</string>
+ <string name="print_service_installed_message" msgid="7005672469916968131">"יש להקיש כדי להפעיל את השירות"</string>
+ <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"יש להזין את קוד האימות של מנהל המכשיר"</string>
<string name="restr_pin_enter_pin" msgid="373139384161304555">"הזן קוד גישה"</string>
<string name="restr_pin_incorrect" msgid="3861383632940852496">"שגוי"</string>
<string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"קוד גישה נוכחי"</string>
- <string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"קוד גישה חדש"</string>
+ <string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"קוד אימות חדש"</string>
<string name="restr_pin_confirm_pin" msgid="7143161971614944989">"אשר את קוד הגישה החדש"</string>
<string name="restr_pin_create_pin" msgid="917067613896366033">"צור קוד גישה לשינוי הגבלות"</string>
- <string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"קודי הגישה לא תואמים. נסה שוב."</string>
+ <string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"קודי האימות לא תואמים. יש לנסות שוב."</string>
<string name="restr_pin_error_too_short" msgid="1547007808237941065">"קוד הגישה קצר מדי. חייב להיות באורך 4 ספרות לפחות."</string>
<plurals name="restr_pin_countdown" formatted="false" msgid="4427486903285216153">
<item quantity="two">נסה שוב בעוד <xliff:g id="COUNT">%d</xliff:g> שניות</item>
@@ -1857,7 +1856,7 @@
<item quantity="other">נסה שוב בעוד <xliff:g id="COUNT">%d</xliff:g> שניות</item>
<item quantity="one">נסה שוב בעוד שנייה אחת</item>
</plurals>
- <string name="restr_pin_try_later" msgid="5897719962541636727">"נסה שוב מאוחר יותר"</string>
+ <string name="restr_pin_try_later" msgid="5897719962541636727">"יש לנסות שוב מאוחר יותר"</string>
<string name="immersive_cling_title" msgid="2307034298721541791">"צפייה במסך מלא"</string>
<string name="immersive_cling_description" msgid="7092737175345204832">"כדי לצאת, פשוט מחליקים אצבע מלמעלה למטה."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"הבנתי"</string>
@@ -1865,9 +1864,9 @@
<string name="hour_picker_description" msgid="5153757582093524635">"מחוון שעות מעגלי"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"מחוון דקות מעגלי"</string>
<string name="select_hours" msgid="5982889657313147347">"בחר שעות"</string>
- <string name="select_minutes" msgid="9157401137441014032">"בחר דקות"</string>
+ <string name="select_minutes" msgid="9157401137441014032">"בחירת דקות"</string>
<string name="select_day" msgid="2060371240117403147">"בחר חודש ויום"</string>
- <string name="select_year" msgid="1868350712095595393">"בחר שנה"</string>
+ <string name="select_year" msgid="1868350712095595393">"בחירת שנה"</string>
<string name="deleted_key" msgid="9130083334943364001">"<xliff:g id="KEY">%1$s</xliff:g> נמחק"</string>
<string name="managed_profile_label_badge" msgid="6762559569999499495">"עבודה <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_2" msgid="5673187309555352550">"<xliff:g id="LABEL">%1$s</xliff:g> שני בעבודה"</string>
@@ -2014,7 +2013,7 @@
<string name="profile_encrypted_message" msgid="1128512616293157802">"הקש לביטול נעילת פרופיל העבודה"</string>
<string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"מחובר אל <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
<string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"הקש כדי להציג קבצים"</string>
- <string name="pin_target" msgid="8036028973110156895">"הצמד"</string>
+ <string name="pin_target" msgid="8036028973110156895">"הצמדה"</string>
<string name="pin_specific_target" msgid="7824671240625957415">"הצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="unpin_target" msgid="3963318576590204447">"בטל הצמדה"</string>
<string name="unpin_specific_target" msgid="3859828252160908146">"ביטול ההצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string>
@@ -2034,12 +2033,12 @@
<string name="app_category_maps" msgid="6395725487922533156">"מפות וניווט"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"פרודוקטיביות"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"נגישות"</string>
- <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"שטח האחסון במכשיר"</string>
+ <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"נפח האחסון במכשיר"</string>
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"ניקוי באגים ב-USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"שעה"</string>
<string name="time_picker_minute_label" msgid="8307452311269824553">"דקה"</string>
<string name="time_picker_header_text" msgid="9073802285051516688">"הגדרת שעה"</string>
- <string name="time_picker_input_error" msgid="8386271930742451034">"הזן שעה חוקית"</string>
+ <string name="time_picker_input_error" msgid="8386271930742451034">"יש להזין שעה חוקית"</string>
<string name="time_picker_prompt_label" msgid="303588544656363889">"מהי השעה הנכונה"</string>
<string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"העבר למצב קלט טקסט לצורך הזנת השעה"</string>
<string name="time_picker_radial_mode_description" msgid="1222342577115016953">"העבר למצב שעון לצורך הזנת השעה"</string>
@@ -2079,7 +2078,7 @@
<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="notification_reply_button_accessibility" msgid="5235776156579456126">"השב"</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>
<string name="mmcc_imsi_unknown_in_hlr" msgid="227760698553988751">"ניהול התצורה של כרטיס ה-SIM לא מתאים לזיהוי קולי"</string>
@@ -2315,8 +2314,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"פרטיות חיישנים"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"סמל האפליקציה"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"תדמית המותג של האפליקציה"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"בדיקה של הגדרות הגישה"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"לשירות <xliff:g id="SERVICE_NAME">%s</xliff:g> יהיו הרשאות לצפייה ולשליטה במסך. יש להקיש כדי לבדוק."</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 5edede8..95a0f86 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"実行中のアプリ"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"電池を消費しているアプリ"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"拡大"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"ユーザー補助機能に関するセキュリティ ポリシー"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」が電池を使用しています"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 個のアプリが電池を使用しています"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"タップして電池やデータの使用量を確認"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ショートカットを使用"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"色反転"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色補正"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"明るさを下げる"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"さらに輝度を下げる"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が ON になりました。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が OFF になりました。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> を使用するには、音量大と音量小の両方のボタンを 3 秒間長押ししてください"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"センサー プライバシー"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"アプリのアイコン"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"アプリのブランド イメージ"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"アクセス設定の確認"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> は画面を参照、操作できます。タップしてご確認ください。"</string>
</resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 784bdbe..4f27b37 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"აპი გაშვებულია"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"ბატარეის მხარჯავი აპები"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"გადიდება"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"მარტივი წვდომის უსაფრთხოების წესები"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> იყენებს ბატარეას"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"ბატარეას <xliff:g id="NUMBER">%1$d</xliff:g> აპი იყენებს"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"შეეხეთ ბატარეისა და მონაცემების მოხმარების შესახებ დეტალური ინფორმაციისთვის"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"მალსახმობის გამოყენება"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ფერთა ინვერსია"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ფერთა კორექცია"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"სიკაშკაშის შემცირება"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"დამატებითი დაბნელება"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ჩართულია."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> გამორთულია."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> რომ გამოიყენოთ, დააჭირეთ ხმის ორივე ღილაკზე 3 წამის განმავლობაში"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"სენსორის კონფიდენციალურობა"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"აპლიკაციის ხატულა"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"აპლიკაციის ბრენდის სურათი"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"სწრაფი წვდომის პარამეტრები"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g>-ს შეუძლია თქვენი ეკრანის ნახვა და მართვა. შეეხეთ გადასახედად."</string>
</resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 8065638..df1bbeb 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Қолданба қосулы"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Батареяны пайдаланып жатқан қолданбалар"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ұлғайту"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Арнайы мүмкіндіктер қауіпсіздігі саясаты"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> батареяны пайдалануда"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> қолданба батареяны пайдалануда"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батарея мен деректер трафигі туралы білу үшін түртіңіз"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Төте жолды пайдалану"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Түстер инверсиясы"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Түсті түзету"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Жарықтығын азайту"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Аса күңгірт"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қосулы."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дыбыс деңгейі пернелерін басып тұрған соң, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өшірілді."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін пайдалану үшін дыбыс деңгейін реттейтін екі түймені де 3 секунд басып тұрыңыз"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Датчикке қатысты құпиялылық"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Қолданба белгішесі"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Қолданба брендін ілгері жылжыту кескіні"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Пайдалану параметрлерін тексеріңіз"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> қызметі экраныңызды көріп, бақылай алады. Көру үшін түртіңіз."</string>
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 7b2f618..e8f45bc 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"កម្មវិធីដែលកំពុងដំណើរការ"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"កម្មវិធីដែលកំពុងប្រើថ្ម"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"ការពង្រីក"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"គោលការណ៍ស្ដីពីសុវត្ថិភាពនៃភាពងាយស្រួល"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងប្រើថ្ម"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"កម្មវិធីចំនួន <xliff:g id="NUMBER">%1$d</xliff:g> កំពុងប្រើថ្ម"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ចុចដើម្បីមើលព័ត៌មានលម្អិតអំពីការប្រើប្រាស់ទិន្នន័យ និងថ្ម"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ប្រើប្រាស់ផ្លូវកាត់"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"បញ្ច្រាសពណ៌"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ការកែពណ៌"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"បន្ថយពន្លឺ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ពន្លឺតិចខ្លាំង"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"ចុចគ្រាប់ចុចកម្រិតសំឡេងទាំងពីរឱ្យជាប់រយៈពេលបីវិនាទី ដើម្បីប្រើ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ឯកជនភាពឧបករណ៍ចាប់សញ្ញា"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"រូបកម្មវិធី"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"រូបភាពផ្សព្វផ្សាយម៉ាកកម្មវិធី"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"ពិនិត្យមើលការកំណត់សិទ្ធិចូលប្រើ"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> អាចមើល និងគ្រប់គ្រងអេក្រង់របស់អ្នកបាន។ សូមចុច ដើម្បីពិនិត្យមើល។"</string>
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 5fd7629..cfa6cf8 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App ರನ್ ಆಗುತ್ತಿದೆ"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"ಅಪ್ಲಿಕೇಶನ್ಗಳು ಬ್ಯಾಟರಿಯನ್ನು ಉಪಯೋಗಿಸುತ್ತಿವೆ"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"ಹಿಗ್ಗಿಸುವಿಕೆ"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"ಪ್ರವೇಶಿಸುವಿಕೆ ಭದ್ರತಾ ನೀತಿ"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಆ್ಯಪ್, ಬ್ಯಾಟರಿಯನ್ನು ಬಳಸುತ್ತಿದೆ"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ಅಪ್ಲಿಕೇಶನ್ಗಳು ಬ್ಯಾಟರಿ ಬಳಸುತ್ತಿವೆ"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ಬ್ಯಾಟರಿ,ಡೇಟಾ ಬಳಕೆಯ ವಿವರಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
@@ -1690,7 +1689,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ಶಾರ್ಟ್ಕಟ್ ಬಳಸಿ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ಬಣ್ಣ ವಿಲೋಮ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"ಪ್ರಖರತೆಯನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (3222994553174604132) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಲಾಗಿದೆ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಬಳಸಲು ಎರಡೂ ಧ್ವನಿ ಕೀಗಳನ್ನು ಮೂರು ಸೆಕೆಂಡ್ಗಳ ಕಾಲ ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
@@ -2247,8 +2247,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ಸೆನ್ಸರ್ ಗೌಪ್ಯತೆ"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ಅಪ್ಲಿಕೇಶನ್ ಐಕಾನ್"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ಅಪ್ಲಿಕೇಶನ್ ಬ್ರ್ಯಾಂಡಿಂಗ್ ಚಿತ್ರ"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"ಪ್ರವೇಶ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಪರಿಶೀಲಿಸಿ"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಅನ್ನು ವೀಕ್ಷಿಸಬಹುದು ಮತ್ತು ನಿಯಂತ್ರಿಸಬಹುದು. ಪರಿಶೀಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 340cc62..08ceb30 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"실행 중인 앱"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"배터리를 소모하는 앱"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"확대"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"접근성 보안 정책"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 배터리 사용 중"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"앱 <xliff:g id="NUMBER">%1$d</xliff:g>개에서 배터리 사용 중"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"탭하여 배터리 및 데이터 사용량 확인"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"단축키 사용"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"색상 반전"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"색상 보정"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"밝기 낮추기"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"더 어둡게"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 서비스를 사용하려면 두 볼륨 키를 3초 동안 길게 누르세요"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"센서 개인정보 보호"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"애플리케이션 아이콘"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"애플리케이션 브랜드 이미지"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"접근성 설정 확인"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> 서비스가 내 화면을 보고 제어할 수 있습니다. 검토하려면 탭하세요."</string>
</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 2a30a73..034d94e 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Колдонмо иштеп жатат"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Колдонмолор батареяңызды коротууда"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Чоңойтуу"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Атайын мүмкүнчүлүктөрдүн коопсуздугун коргоо саясаты"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу батареяны пайдаланып жатат"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> колдонмо батареяны пайдаланып жатат"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батареянын кубаты жана трафиктин көлөмү жөнүндө билүү үчүн таптап коюңуз"</string>
@@ -1217,12 +1216,12 @@
<string name="launch_warning_replace" msgid="3073392976283203402">"<xliff:g id="APP_NAME">%1$s</xliff:g> азыр иштеп жатат."</string>
<string name="launch_warning_original" msgid="3332206576800169626">"Башында <xliff:g id="APP_NAME">%1$s</xliff:g> жүргүзүлгөн."</string>
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"Шкала"</string>
- <string name="screen_compat_mode_show" msgid="5080361367584709857">"Ар дайым көрсөтүлсүн"</string>
+ <string name="screen_compat_mode_show" msgid="5080361367584709857">"Ар дайым көрүнсүн"</string>
<string name="screen_compat_mode_hint" msgid="4032272159093750908">"Муну тутум жөндөөлөрүнөн кайра иштетүү > Колдонмолор > Жүктөлүп алынган."</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу көрүнүштүн тандалган өлчөмүн экранда көрсөтө албайт жана туура эмес иштеши мүмкүн."</string>
- <string name="unsupported_display_size_show" msgid="980129850974919375">"Ар дайым көрсөтүлсүн"</string>
+ <string name="unsupported_display_size_show" msgid="980129850974919375">"Ар дайым көрүнсүн"</string>
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android OS тутуму менен иштеген түзмөктүн шайкеш келбеген версиясы үчүн орнотулган колдонмо жана туура эмес иштеши мүмкүн. Колдонмонун жаңыртылган версиясы жеткиликтүү болушу мүмкүн."</string>
- <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Ар дайым көрсөтүлсүн"</string>
+ <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Ар дайым көрүнсүн"</string>
<string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Жаңыртууну издөө"</string>
<string name="smv_application" msgid="3775183542777792638">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу (<xliff:g id="PROCESS">%2$s</xliff:g> процесси) өз алдынча иштеткен StrictMode саясатын бузду."</string>
<string name="smv_process" msgid="1398801497130695446">"<xliff:g id="PROCESS">%1$s</xliff:g> процесси өзүнүн мажбурланган StrictMode саясатын бузуп койду."</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Кыска жолду колдонуу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Түстү инверсиялоо"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Түсүн тууралоо"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Экрандын жарыктыгын төмөндөтүү"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Кошумча караңгылатуу"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> күйгүзүлдү."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өчүрүлдү."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын колдонуу үчүн үнүн чоңойтуп/кичирейтүү баскычтарын үч секунд коё бербей басып туруңуз"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Сенсордун купуялыгы"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Колдонмонун сүрөтчөсү"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Колдонмонун брендинин сүрөтү"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Кирүү мүмкүнчүлүгүнүн жөндөөлөрүн текшериңиз"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> экраныңызды көрүп, көзөмөлдөй алат. Көрүү үчүн таптап коюңуз."</string>
</resources>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 42c2c69..ca549ae 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -30,9 +30,7 @@
<!-- Height of the status bar -->
<dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen>
<!-- Height of area above QQS where battery/time go -->
- <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
- <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
- <dimen name="quick_qs_total_height">152dp</dimen>
+ <dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Default height of an action bar. -->
<dimen name="action_bar_default_height">40dip</dimen>
<!-- Vertical padding around action bar icons. -->
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index aadf13f..ad56f88 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ແອັບກຳລັງເຮັດວຽກ"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"ແອັບທີ່ກຳລັງໃຊ້ແບັດເຕີຣີ"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"ການຂະຫຍາຍ"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"ນະໂຍບາຍຄວາມປອດໄພການຊ່ວຍເຂົ້າເຖິງ"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງໃຊ້ແບັດເຕີຣີຢູ່"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ແອັບກຳລັງໃຊ້ແບັດເຕີຣີຢູ່"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ແຕະເພື່ອເບິ່ງລາຍລະອຽດການນຳໃຊ້ແບັດເຕີຣີ ແລະ ອິນເຕີເນັດ"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ໃຊ້ປຸ່ມລັດ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ການປີ້ນສີ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ການແກ້ໄຂຄ່າສີ"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"ຫຼຸດຄວາມສະຫວ່າງລົງ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ຫຼຸດແສງເປັນພິເສດ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ເປີດໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ແລ້ວ."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ປິດ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ໄວ້ແລ້ວ."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"ກົດປຸ່ມສຽງທັງສອງພ້ອມກັນຄ້າງໄວ້ສາມວິນາທີເພື່ອໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ຄວາມເປັນສ່ວນຕົວເຊັນເຊີ"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ໄອຄອນແອັບພລິເຄຊັນ"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ຮູບແບຣນແອັບພລິເຄຊັນ"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"ກວດສອບການຕັ້ງຄ່າການເຂົ້າເຖິງ"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ສາມາດເບິ່ງ ແລະ ຄວບຄຸມໜ້າຈໍຂອງທ່ານໄດ້. ແຕະເພື່ອກວດສອບ."</string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 6e6c561..1ae2996 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -300,8 +300,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Programa paleista"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programos, naudojančios akumuliatoriaus energiją"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Didinimas"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Pritaikomumo saugos politika"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ naudoja akumuliatoriaus energiją"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Programų, naudojančių akumuliatoriaus energiją: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Palieskite ir sužinokite išsamios informacijos apie akumuliatoriaus bei duomenų naudojimą"</string>
@@ -1734,7 +1733,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Naudoti spartųjį klavišą"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Spalvų inversija"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Spalvų taisymas"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Šviesumo mažinimas"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Itin blanku"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ įjungta."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ išjungta."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Jei norite naudoti „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“, paspauskite abu garsumo klavišus ir palaikykite tris sekundes"</string>
@@ -2315,8 +2314,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Jutiklių privatumas"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Programos piktograma"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Programos prekės ženklo vaizdas"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Patikrinkite prieigos nustatymus"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"„<xliff:g id="SERVICE_NAME">%s</xliff:g>“ gali peržiūrėti ir valdyti jūsų ekraną. Palieskite ir peržiūrėkite."</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index ae532ee..f011815 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -297,8 +297,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Lietotne darbojas"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Lietotnes, kas patērē akumulatora jaudu"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Palielinājums"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Pieejamības drošības politika"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> izmanto akumulatoru"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> lietotne(-es) izmanto akumulatoru"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Pieskarieties, lai skatītu detalizētu informāciju par akumulatora un datu lietojumu"</string>
@@ -1712,7 +1711,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Izmantot saīsni"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Krāsu inversija"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Krāsu korekcija"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Spilgtuma samazināšana"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Papildu aptumšošana"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika ieslēgts."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika izslēgts."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Lai izmantotu pakalpojumu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, nospiediet abus skaļuma taustiņus un turiet tos trīs sekundes."</string>
@@ -2281,8 +2280,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensoru konfidencialitāte"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Lietojumprogrammas ikona"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Lietojumprogrammas zīmola attēls"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Pārbaudiet piekļuves iestatījumus"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"Pakalpojums <xliff:g id="SERVICE_NAME">%s</xliff:g> var skatīt un kontrolēt jūsu ekrānu. Pieskarieties, lai to pārskatītu."</string>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 725d972..bbd9d9d 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Апликацијата работи"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Апликации што ја трошат батеријата"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Зголемување"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Политика за безбедност на „Пристапност“"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> користи батерија"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> апликации користат батерија"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Допрете за детали за батеријата и потрошениот сообраќај"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи кратенка"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија на бои"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција на бои"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Намалување на осветленоста"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнително затемнување"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е вклучена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е исклучена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Притиснете ги и задржете ги двете копчиња за јачина на звукот во траење од три секунди за да користите <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Приватност на сензорот"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Икона за апликацијата"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Слика за брендирање на апликацијата"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Проверете ги поставките за пристап"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> може да го прегледува и контролира вашиот екран. Допрете за да прегледате."</string>
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index a8457c8..cfc112a 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ആപ്പ് പ്രവർത്തിക്കുന്നു"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"ആപ്പുകൾ ബാറ്ററി ഉപയോഗിക്കുന്നു"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"മാഗ്നിഫിക്കേഷൻ"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"ഉപയോഗസഹായി സുരക്ഷാ നയം"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബാറ്ററി ഉപയോഗിക്കുന്നു"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ആപ്പുകൾ ബാറ്ററി ഉപയോഗിക്കുന്നു"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ബാറ്ററി, ഡാറ്റ ഉപയോഗം എന്നിവയുടെ വിശദാംശങ്ങളറിയാൻ ടാപ്പുചെയ്യുക"</string>
@@ -1251,9 +1250,9 @@
<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_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="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>
<string name="volume_ringtone" msgid="134784084629229029">"റിംഗർ വോളിയം"</string>
<string name="volume_music" msgid="7727274216734955095">"മീഡിയ വോളിയം"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"കുറുക്കുവഴി ഉപയോഗിക്കുക"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"വർണ്ണ വിപര്യയം"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"നിറം ക്രമീകരിക്കൽ"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"തെളിച്ചം കുറയ്ക്കുക"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"കൂടുതൽ ഡിം ചെയ്യൽ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"വോളിയം കീകൾ പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓണാക്കി."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"വോളിയം കീകൾ അമർത്തിപ്പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫാക്കി."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഉപയോഗിക്കാൻ, രണ്ട് വോളിയം കീകളും മൂന്ന് സെക്കൻഡ് അമർത്തിപ്പിടിക്കുക"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"സെൻസർ സ്വകാര്യത"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ആപ്പ് ഐക്കൺ"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"അപ്ലിക്കേഷൻ ബ്രാൻഡിംഗ് ഇമേജ്"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"ആക്സസ് ക്രമീകരണം പരിശോധിക്കുക"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> എന്നതിന് നിങ്ങളുടെ സ്ക്രീൻ കാണാനും നിയന്ത്രിക്കാനും കഴിയും. അവലോകനം ചെയ്യുന്നതിന് ടാപ്പ് ചെയ്യുക."</string>
</resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 00fef20..99c4628 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Апп ажиллаж байна"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Апп батарей ашиглаж байна"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Томруулах"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Хандалтын аюулгүй байдлын бодлого"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> батерей ашиглаж байна"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> апп батерей ашиглаж байна"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батерей, дата ашиглалтын талаар дэлгэрэнгүйг харахын тулд товшино уу"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Товчлол ашиглах"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Өнгө хувиргалт"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Өнгөний засвар"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Гэрэлтүүлгийг багасгах"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Хэт бүүдгэр"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаалаа."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраалаа."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г ашиглахын тулд дууны түвшнийг ихэсгэх, багасгах түлхүүрийг 3 секундийн турш зэрэг дарна уу"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Мэдрэгчийн нууцлал"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Аппын дүрс тэмдэг"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Аппын брэнд зураг"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Хандалтын тохиргоог шалгана уу"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> таны дэлгэцийг харах болон хянах боломжтой. Хянахын тулд товшино уу."</string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 87d32d4..adb86de 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"APP चालत आहे"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"बॅटरी लवकर संपवणारी अॅप्स"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"मॅग्निफिकेशन"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"अॅक्सेसिबिलिटी सुरक्षा धोरण"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> बॅटरी वापरत आहे"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> अॅप्स बॅटरी वापरत आहेत"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"बॅटरी आणि डेटा वापराच्या तपशीलांसाठी टॅप करा"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट वापरा"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रंगांची उलटापालट"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रंग सुधारणा"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"ब्राइटनेस कमी करा"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"आणखी डिम"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू केला आहे."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केले आहे."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> वापरण्यासाठी दोन्ही व्हॉल्युम की तीन सेकंद दाबा आणि धरून ठेवा"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेन्सरशी संबंधित गोपनीयतेबाबत सूचना"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ॲप्लिकेशन आयकन"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"अॅप्लिकेशन ब्रॅंडिंग इमेज"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"अॅक्सेसशी संबंधित सेटिंग्ज तपासा"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> हे तुमची स्क्रीन पाहू शकते आणि नियंत्रित करू शकते. परीक्षण करण्यासाठी टॅप करा."</string>
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 2da9314..9550abb 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Apl berjalan"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apl yang menggunakan bateri"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Pembesaran"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Dasar keselamatan kebolehaksesan"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang menggunakan bateri"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apl sedang menggunakan bateri"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ketik untuk mendapatkan butiran tentang penggunaan kuasa bateri dan data"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gunakan Pintasan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Penyongsangan Warna"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Pembetulan Warna"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Kurangkan kecerahan"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Amat malap"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dihidupkan."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dimatikan."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tekan dan tahan kedua-dua kekunci kelantangan selama tiga saat untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privasi Penderia"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikon aplikasi"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imej jenama aplikasi"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Semak tetapan akses"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> boleh melihat dan mengawal skrin anda. Ketik untuk menyemak."</string>
</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 92ec446..165db32 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"APP လုပ်ဆောင်နေသည်"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"အက်ပ်များက ဘက်ထရီကုန်စေသည်"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"ချဲ့ခြင်း"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ လုံခြုံရေးမူဝါဒ"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> က ဘက်ထရီကို အသုံးပြုနေသည်"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"အက်ပ် <xliff:g id="NUMBER">%1$d</xliff:g> ခုက ဘက်ထရီကို အသုံးပြုနေသည်"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ဘက်ထရီနှင့် ဒေတာအသုံးပြုမှု အသေးစိတ်ကို ကြည့်ရန် တို့ပါ"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ဖြတ်လမ်းလင့်ခ်ကို သုံးရန်"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"အရောင် ပြောင်းပြန်လှန်ခြင်း"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"အရောင်ပြင်ဆင်ခြင်း"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"တောက်ပမှုကို လျှော့ခြင်း"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ပိုမှိန်ခြင်း"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ဖွင့်လိုက်သည်။"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ပိတ်လိုက်သည်။"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို သုံးရန် အသံအတိုးအလျှော့ ခလုတ်နှစ်ခုလုံးကို သုံးစက္ကန့်ကြာ ဖိထားပါ"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"အာရုံခံကိရိယာ လုံခြုံရေး"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"အပလီကေးရှင်း သင်္ကေတ"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"အပလီကေးရှင်း ကုန်အမှတ်တံဆိပ်ပုံ"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"အသုံးပြုခွင့် ဆက်တင်များကို စစ်ဆေးပါ"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> က သင့်ဖန်သားပြင်ကို ကြည့်ရှုပြီး ထိန်းချုပ်နိုင်သည်။ ပြန်ကြည့်ရန် တို့ပါ။"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 8a39a87..b2b4675 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App kjører"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apper bruker batteri"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Forstørring"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Sikkerhetsretningslinjer for tilgjengelighet"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> bruker batteri"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apper bruker batteri"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Trykk for detaljer om batteri- og databruk"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Bruk snarveien"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Fargeinvertering"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Fargekorrigering"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduser lysstyrken"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra dempet belysning"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått på."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått av."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Trykk og hold inne begge volumtastene i tre sekunder for å bruke <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorpersonvern"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Appikon"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Merkevareprofilen til appen"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Sjekk tilgangsinnstillingene"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> kan se og kontrollere skjermen. Trykk for å gjennomgå."</string>
</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 96d7827..64d825c 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"एप चलिरहेको छ"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"एपहरूले ब्याट्री खपत गर्दै छन्"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"जुम इन गर्ने सुविधा"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"सर्वसुलभताको सुरक्षासम्बन्धी नीति"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले ब्याट्री प्रयोग गर्दै छ"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> एपहरूले ब्याट्री प्रयोग गर्दै छन्"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ब्याट्री र डेटाका प्रयोग सम्बन्धी विवरणहरूका लागि ट्याप गर्नुहोस्"</string>
@@ -1129,7 +1128,7 @@
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="1532369154488982046">"सबैलाई चयन गर्नुहोस्"</string>
<string name="cut" msgid="2561199725874745819">"काट्नुहोस्"</string>
- <string name="copy" msgid="5472512047143665218">"प्रतिलिपि बनाउनुहोस्"</string>
+ <string name="copy" msgid="5472512047143665218">"कपी गर्नुहोस्"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"क्लिपबोर्डमा प्रतिलिपि गर्न सकिएन"</string>
<string name="paste" msgid="461843306215520225">"टाँस्नुहोस्"</string>
<string name="paste_as_plain_text" msgid="7664800665823182587">"सामान्य पाठको रूपमा टाँस्नुहोस्"</string>
@@ -1690,7 +1689,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"सर्टकट प्रयोग गर्नुहोस्"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रङ्ग उल्टाउने सुविधा"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रङ्ग सच्याउने सुविधा"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"चमक घटाइयोस्"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (3222994553174604132) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन भयो।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अफ भयो।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> प्रयोग गर्न दुवै भोल्युम कुञ्जीहरूलाई तीन सेकेन्डसम्म थिचिराख्नुहोस्"</string>
@@ -2247,8 +2247,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेन्सरसम्बन्धी गोपनीयता"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"एप जनाउने आइकन"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"एपको ब्रान्डिङ फोटो"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"हेर्ने तथा नियन्त्रण गर्ने अनुमतिसम्बन्धी सेटिङ जाँच्नु…"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ले तपाईंको स्क्रिन हेर्न र नियन्त्रण गर्न सक्छ। समीक्षा गर्न ट्याप गर्नुहोस्।"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 3084c4e..c0026b7 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1366,7 +1366,7 @@
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"DELEN"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"WEIGEREN"</string>
<string name="select_input_method" msgid="3971267998568587025">"Invoermethode selecteren"</string>
- <string name="show_ime" msgid="6406112007347443383">"Dit op het scherm weergeven terwijl het fysieke toetsenbord actief is"</string>
+ <string name="show_ime" msgid="6406112007347443383">"Op het scherm tonen terwijl het fysieke toetsenbord actief is"</string>
<string name="hardware" msgid="1800597768237606953">"Virtueel toetsenbord tonen"</string>
<string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Fysiek toetsenbord instellen"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Tik om een taal en indeling te selecteren"</string>
@@ -1580,7 +1580,7 @@
<string name="fingerprints" msgid="148690767172613723">"Vingerafdrukken:"</string>
<string name="sha256_fingerprint" msgid="7103976380961964600">"SHA-256-vingerafdruk"</string>
<string name="sha1_fingerprint" msgid="2339915142825390774">"SHA-1-vingerafdruk:"</string>
- <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Alles weergeven"</string>
+ <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Alles tonen"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Een activiteit kiezen"</string>
<string name="share_action_provider_share_with" msgid="1904096863622941880">"Delen met"</string>
<string name="sending" msgid="206925243621664438">"Verzenden..."</string>
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sneltoets gebruiken"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Kleurinversie"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurcorrectie"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Helderheid verlagen"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra gedimd"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is ingeschakeld."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> uitgeschakeld."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Houd beide volumetoetsen drie seconden ingedrukt om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruiken"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index a2c46d1..770e4f0 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ଆପ୍ ଚାଲୁଛି"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"ଆପ୍ଗୁଡ଼ିକ ବ୍ୟାଟେରୀ ଖର୍ଚ୍ଚ କରିଥା\'ନ୍ତି"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"ମ୍ୟାଗ୍ନିଫିକେସନ୍"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"ଆକ୍ସେସିବିଲିଟୀ ସୁରକ୍ଷା ନୀତି"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରୁଛି"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>ଟି ଆପ୍ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରୁଛନ୍ତି"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ବ୍ୟାଟେରୀ ଏବଂ ଡାଟା ବ୍ୟବହାର ଉପରେ ବିବରଣୀ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ଶର୍ଟକଟ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ରଙ୍ଗ ବଦଳାଇବାର ସୁବିଧା"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"ଉଜ୍ଜ୍ୱଳତା କମ୍ କରନ୍ତୁ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ଅତିରିକ୍ତ ଡିମ୍"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ ହୋଇଛି।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ ହୋଇଛି।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍ କୀ ଦବାଇ ଧରି ରଖନ୍ତୁ"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ସେନ୍ସର୍ ଗୋପନୀୟତା"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ଆପ୍ଲିକେସନ୍ ଆଇକନ୍"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ଆପ୍ଲିକେସନ୍ ବ୍ରାଣ୍ଡିଂ ଛବି"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"ଆକ୍ସେସ୍ ସେଟିଂସକୁ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ଦେଖିପାରିବ ଏବଂ ନିୟନ୍ତ୍ରଣ କରିପାରିବ। ସମୀକ୍ଷା କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index b42bd96..c9267e4 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ਚੱਲ ਰਹੀ ਐਪ"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"ਬੈਟਰੀ ਦੀ ਖਪਤ ਕਰਨ ਵਾਲੀਆਂ ਐਪਾਂ"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"ਵੱਡਦਰਸ਼ੀਕਰਨ"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"ਪਹੁੰਚਯੋਗਤਾ ਸੁਰੱਖਿਆ ਨੀਤੀ"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਵੱਲੋਂ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ਐਪਾਂ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਹਨ"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ਬੈਟਰੀ ਅਤੇ ਡਾਟਾ ਵਰਤੋਂ ਸਬੰਧੀ ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
@@ -1690,7 +1689,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ਸ਼ਾਰਟਕੱਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ਰੰਗ ਪਲਟਨਾ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ਰੰਗ ਸੁਧਾਈ"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"ਚਮਕ ਘਟਾਓ"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (3222994553174604132) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ ਕੁੰਜੀਆਂ ਨੂੰ 3 ਸਕਿੰਟਾਂ ਲਈ ਦਬਾਈ ਰੱਖੋ"</string>
@@ -2247,8 +2247,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ਸੈਂਸਰ ਪਰਦੇਦਾਰੀ"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ਐਪਲੀਕੇਸ਼ਨ ਪ੍ਰਤੀਕ"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ਐਪਲੀਕੇਸ਼ਨ ਦਾ ਬ੍ਰਾਂਡ ਵਾਲਾ ਚਿੱਤਰ"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"ਪਹੁੰਚ ਸੈਟਿੰਗਾਂ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ਸੇਵਾ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਦੇਖ ਅਤੇ ਕੰਟਰੋਲ ਕਰ ਸਕਦੀ ਹੈ। ਸਮੀਖਿਆ ਲਈ ਟੈਪ ਕਰੋ।"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 68dd0a1..c78efd7 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -300,8 +300,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Działa aplikacja"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacje zużywające baterię"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Powiększenie"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Zasady bezpieczeństwa związane z ułatwieniami dostępu"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> zużywa baterię"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Liczba aplikacji zużywających baterię: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Kliknij, by wyświetlić szczegóły wykorzystania baterii i użycia danych"</string>
@@ -1734,7 +1733,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Użyj skrótu"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Odwrócenie kolorów"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcja kolorów"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Zmniejsz jasność"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatkowe przyciemnienie"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została włączona."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została wyłączona."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Naciśnij i przytrzymaj oba przyciski głośności przez trzy sekundy, by użyć usługi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2315,8 +2314,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Poufność danych z czujników"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikona aplikacji"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Wizerunek marki aplikacji"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Sprawdź ustawienia dostępu"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"Usługa <xliff:g id="SERVICE_NAME">%s</xliff:g> może wyświetlać i kontrolować ekran. Kliknij, aby sprawdzić."</string>
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 95e7be6..5962304 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduzir brilho"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mais escuro"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 1366379..54d0a2e 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar atalho"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção da cor"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduza o brilho"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecimento extra"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas do volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Prima sem soltar as teclas de volume durante três segundos para utilizar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 95e7be6..5962304 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduzir brilho"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mais escuro"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 513e489..2d7d149 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -297,8 +297,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicația rulează"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicațiile consumă bateria"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Mărire"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Politica de securitate privind accesibilitatea"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> folosește bateria"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicații folosesc bateria"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Atingeți pentru mai multe detalii privind bateria și utilizarea datelor"</string>
@@ -1712,7 +1711,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizați comanda rapidă"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversarea culorilor"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corecția culorii"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduceți luminozitatea"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminozitate redusă suplimentar"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Apăsați ambele butoane de volum timp de trei secunde pentru a folosi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2281,8 +2280,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidențialitatea privind senzorii"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Pictograma aplicației"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imaginea de branding a aplicației"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Verificați setările pentru acces"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> poată să vadă și să vă controleze ecranul. Atingeți pentru a examina."</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 775d807..a980750 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -232,7 +232,7 @@
<string name="reboot_to_update_prepare" msgid="6978842143587422365">"Подготовка обновлений…"</string>
<string name="reboot_to_update_package" msgid="4644104795527534811">"Обработка обновлений…"</string>
<string name="reboot_to_update_reboot" msgid="4474726009984452312">"Перезагрузка…"</string>
- <string name="reboot_to_reset_title" msgid="2226229680017882787">"Сбросить к заводским настройкам"</string>
+ <string name="reboot_to_reset_title" msgid="2226229680017882787">"Сбросить настройки"</string>
<string name="reboot_to_reset_message" msgid="3347690497972074356">"Перезагрузка…"</string>
<string name="shutdown_progress" msgid="5017145516412657345">"Выключение..."</string>
<string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"Планшетный ПК будет отключен."</string>
@@ -300,8 +300,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Приложение активно"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Приложения, расходующие заряд"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Увеличение"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Политика безопасности для специальных возможностей"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" расходует заряд"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Несколько приложений (<xliff:g id="NUMBER">%1$d</xliff:g>) расходуют заряд"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Нажмите, чтобы проверить энергопотребление и трафик"</string>
@@ -1734,7 +1733,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Использовать быстрое включение"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверсия цветов"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Коррекция цвета"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Уменьшение яркости"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнительное уменьшение яркости"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" включена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" отключена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Чтобы использовать сервис \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", нажмите и удерживайте обе клавиши громкости в течение трех секунд."</string>
@@ -2315,8 +2314,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Конфиденциальность датчиков"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Значок приложения"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Образ бренда приложения"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Проверьте настройки доступа"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> может просматривать информацию на вашем экране и управлять им. Нажмите, чтобы узнать подробности."</string>
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 4eae659..a1e3b4a 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1689,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"කෙටිමඟ භාවිතා කරන්න"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"වර්ණ අපවර්තනය"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"වර්ණ නිවැරදි කිරීම"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"දීප්තිය අඩු කරන්න"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"තවත් අඳුරු"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාත්මකයි."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාවිරහිතයි."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> භාවිත කිරීමට හඬ පරිමා යතුරු දෙකම තත්පර තුනකට ඔබාගෙන සිටින්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 57fce0e..f22317e 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -300,8 +300,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikácia je spustená"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikácie spotrebúvajúce batériu"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Zväčšenie"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Pravidlá pre zabezpečenie dostupnosti"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> používa batériu"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Aplikácie (<xliff:g id="NUMBER">%1$d</xliff:g>) používajú batériu"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Klepnutím zobrazíte podrobnosti o batérii a spotrebe dát"</string>
@@ -1734,7 +1733,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použiť skratku"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzia farieb"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Úprava farieb"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Zníženie jasu"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mimoriadne stmavenie"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vypnutá."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Ak chcete používať službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, pridržte tri sekundy oba klávesy hlasitosti"</string>
@@ -2315,8 +2314,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Ochrana súkromia senzorov"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikona aplikácie"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imidž značky aplikácie"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Skontrolujte nastavenia prístupu"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> môže zobraziť a ovládať vašu obrazovku. Skontrolujte to klepnutím."</string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0949f3d..220d802 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -300,8 +300,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikacija se izvaja"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacije, ki porabljajo energijo baterije"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Povečava"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Varnostni pravilnik o funkcijah za ljudi s posebnimi potrebami"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> porablja energijo baterije"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Toliko aplikacij porablja energijo baterije: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dotaknite se za prikaz podrobnosti porabe baterije in prenosa podatkov"</string>
@@ -1734,7 +1733,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Uporabi bližnjico"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija barv"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Popravljanje barv"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Zmanjšanje svetlosti"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Zelo zatemnjeno"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vklopljena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je izklopljena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Za uporabo storitve <xliff:g id="SERVICE_NAME">%1$s</xliff:g> pritisnite obe tipki za glasnost in ju pridržite tri sekunde"</string>
@@ -2315,8 +2314,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Zasebnost pri uporabi tipal"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikona aplikacije"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Podoba blagovne znamke aplikacije"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Preverite nastavitve dostopa"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"Storitev <xliff:g id="SERVICE_NAME">%s</xliff:g> si lahko ogleda in upravlja vaš zaslon. Dotaknite se za pregled."</string>
</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 4220eab..c2c1bb4 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikacioni është në ekzekutim"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacionet që konsumojnë baterinë"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Zmadhimi"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Politika e sigurisë e qasshmërisë"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> po përdor baterinë"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikacione po përdorin baterinë"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Trokit për detaje mbi baterinë dhe përdorimin e të dhënave"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Përdor shkurtoren"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Kthimi i ngjyrës"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korrigjimi i ngjyrës"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Redukto ndriçimin"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Tejet i errët"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i aktivizuar."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. U çaktivizua \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Shtyp dhe mbaj shtypur të dy butonat e volumit për tre sekonda për të përdorur <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privatësia e sensorit"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikona e aplikacionit"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imazhi i vendosjes së aplikacionit të markës"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Kontrollo cilësimet e qasjes"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> mund ta shikojë dhe kontrollojë ekranin tënd. Trokit për ta rishikuar."</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 158ee5b..922c95ba 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1711,7 +1711,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи пречицу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија боја"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција боја"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Смањите осветљеност"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Додатно затамњено"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је укључена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је искључена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Притисните и задржите оба тастера за јачину звука три секунде да бисте користили <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 527f203..cf44ee0 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App körs"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Appar som drar batteri"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Förstoring"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Säkerhetspolicy för tillgänglighet"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> drar batteri"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> appar drar batteri"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tryck för information om batteri- och dataanvändning"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Använd kortkommandot"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverterade färger"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Färgkorrigering"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Minska ljusstyrkan"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extradimmat"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tryck och håll båda volymknapparna i tre sekunder för att använda <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorintegritet"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Appikon"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Appens varumärkesbild"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Kontrollera åtkomstinställningar"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> kan visa och styra din skärm. Tryck för att granska."</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 855e225..f715948 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Programu inaendelea kutekelezwa"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programu zinazotumia betri"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ukuzaji"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Sera ya usalama wa ufikivu"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumia betri"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Programu <xliff:g id="NUMBER">%1$d</xliff:g> zinatumia betri"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Gusa ili upate maelezo kuhusu betri na matumizi ya data"</string>
@@ -1004,7 +1003,7 @@
<string name="save_password_never" msgid="6776808375903410659">"Katu"</string>
<string name="open_permission_deny" msgid="5136793905306987251">"Hauna idhini ya kufungua ukurasa huu."</string>
<string name="text_copied" msgid="2531420577879738860">"Maandishi yamenakiliwa kwenye ubao wa kunakili."</string>
- <string name="copied" msgid="4675902854553014676">"Imenakiliwa"</string>
+ <string name="copied" msgid="4675902854553014676">"Umenakili"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika kutoka <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
<string name="pasted_from_clipboard" msgid="6295556725844421812">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika kutoka ubao wa kunakili"</string>
<string name="more_item_label" msgid="7419249600215749115">"Zaidi"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tumia Njia ya Mkato"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ugeuzaji rangi"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Usahihishaji wa rangi"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Punguza ung\'aavu"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Kipunguza mwangaza zaidi"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Vitufe vya sauti vilivyoshikiliwa. Umewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Vitufe vya sauti vimeshikiliwa. Umezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Bonyeza na ushikilie vitufe vyote viwili vya sauti kwa sekunde tatu ili utumie <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Faragha ya Kitambuzi"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Aikoni ya programu"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Picha ya kuweka chapa kwenye programu"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Angalia mipangilio ya kufikia"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> inaweza kuangalia na kudhibiti skrini yako. Gusa ili ukague."</string>
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 0b24c59..0a5aca5 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ஆப்ஸ் இயங்குகிறது"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"பேட்டரியைப் பயன்படுத்தும் ஆப்ஸ்"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"பெரிதாக்கல்"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"அணுகல்தன்மை பாதுகாப்புக் கொள்கை"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் பேட்டரியைப் பயன்படுத்துகிறது"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ஆப்ஸ் பேட்டரியைப் பயன்படுத்துகின்றன"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"பேட்டரி மற்றும் டேட்டா உபயோக விவரங்களைக் காண, தட்டவும்"</string>
@@ -1396,7 +1395,7 @@
<string name="ext_media_unsupported_notification_title" msgid="4358280700537030333">"ஆதரிக்கப்படாத <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_unsupported_notification_title" product="automotive" msgid="6004193172658722381">"<xliff:g id="NAME">%s</xliff:g> வேலை செய்யவில்லை"</string>
<string name="ext_media_unsupported_notification_message" msgid="917738524888367560">"சாதனம் இந்த <xliff:g id="NAME">%s</xliff:g>ஐ ஆதரிக்கவில்லை. ஆதரிக்கப்படும் வடிவமைப்பில் அமைக்க, தட்டவும்."</string>
- <string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"ஆதரிக்கப்படும் வடிவத்தில் <xliff:g id="NAME">%s</xliff:g>ஐ அமைக்கத் தேர்ந்தெடுங்கள்."</string>
+ <string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"ஆதரிக்கப்படும் வடிவத்தில் <xliff:g id="NAME">%s</xliff:g> ஐ அமைக்கத் தேர்ந்தெடுங்கள்."</string>
<string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"சாதனத்தை ரீஃபார்மேட் செய்ய வேண்டியிருக்கும்"</string>
<string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"<xliff:g id="NAME">%s</xliff:g> அகற்றப்பட்டது"</string>
<string name="ext_media_badremoval_notification_message" msgid="1986514704499809244">"உள்ளடக்கத்தை இழக்காமலிருக்க, அகற்றும் முன்பாக மீடியாவை வெளியேற்றவும்"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ஷார்ட்கட்டைப் பயன்படுத்து"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"கலர் இன்வெர்ஷன்"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"வண்ணத் திருத்தம்"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"ஒளிர்வைக் குறைத்தல்"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"மிகக் குறைவான வெளிச்சம்"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆன் செய்யப்பட்டது."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆஃப் செய்யப்பட்டது."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐப் பயன்படுத்த 3 விநாடிகளுக்கு இரண்டு ஒலியளவு பட்டன்களையும் அழுத்திப் பிடிக்கவும்"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"சென்சார் தனியுரிமை"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ஆப்ஸ் ஐகான்"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ஆப்ஸ் பிராண்டிங் இமேஜ்"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"அணுகல் அமைப்புகளைச் சரிபாருங்கள்"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> சேவையால் உங்கள் திரையைப் பார்க்கவும் கட்டுப்படுத்தவும் முடியும். பார்க்கத் தட்டவும்."</string>
</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 60cf91f..a354380 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"యాప్ అమలవుతోంది"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"బ్యాటరీని ఉపయోగిస్తున్న యాప్లు"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"మాగ్నిఫికేషన్"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"యాక్సెసిబిలిటీ సెక్యూరిటీ పాలసీ"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> బ్యాటరీని ఉపయోగిస్తోంది"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> యాప్లు బ్యాటరీని ఉపయోగిస్తున్నాయి"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"బ్యాటరీ మరియు డేటా వినియోగ వివరాల కోసం నొక్కండి"</string>
@@ -1690,7 +1689,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"సత్వరమార్గాన్ని ఉపయోగించు"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"కలర్ మార్పిడి"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"కలర్ సరిచేయడం"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"ప్రకాశాన్ని తగ్గించండి"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (3222994553174604132) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ని ఉపయోగించడానికి వాల్యూమ్ కీలు రెండింటినీ 3 సెకన్లు నొక్కి ఉంచండి"</string>
@@ -2247,8 +2247,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"సెన్సార్ గోప్యత"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"యాప్ చిహ్నం"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"యాప్ బ్రాండింగ్ ఇమేజ్"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"యాక్సెస్ సెట్టింగ్లను చెక్ చేయండి"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> మీ స్క్రీన్ను చూడవచ్చు, నియంత్రించవచ్చు. రివ్యూ చేయడానికి ట్యాప్ చేయండి."</string>
</resources>
diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml
index 176e89e..b023c75 100644
--- a/core/res/res/values-television/themes.xml
+++ b/core/res/res/values-television/themes.xml
@@ -14,14 +14,14 @@
limitations under the License.
-->
<resources>
- <style name="Theme.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+ <style name="Theme.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<style name="Theme.Dialog.Confirmation" parent="Theme.Leanback.Dialog.Confirmation" />
<style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
- <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+ <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<style name="Theme.Material.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
- <style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+ <style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<style name="Theme.Material.Settings.Dialog.Alert" parent="Theme.Leanback.Settings.Dialog.Alert" />
<style name="Theme.Material.Dialog" parent="Theme.Leanback.Dialog" />
- <style name="Theme.Material.Light.Dialog" parent="Theme.Leanback.Light.Dialog" />
+ <style name="Theme.Material.Light.Dialog" parent="Theme.Leanback.Dialog" />
<style name="Theme.Material.Settings.Dialog" parent="Theme.Leanback.Settings.Dialog" />
</resources>
diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml
index d6bdeee..9d0e522 100644
--- a/core/res/res/values-television/themes_device_defaults.xml
+++ b/core/res/res/values-television/themes_device_defaults.xml
@@ -16,7 +16,7 @@
<resources>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" />
- <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+ <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<!-- TODO(b/116457731): remove colorBackground from colors_material.xml if not used anymore -->
<style name="Theme.DeviceDefault.Autofill" parent="Theme.Material">
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index aab52ef..a2bdfa7 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"แอปที่ทำงานอยู่"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"แอปหลายแอปกำลังใช้แบตเตอรี่"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"การขยาย"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"นโยบายความปลอดภัยสำหรับการช่วยเหลือพิเศษ"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังใช้แบตเตอรี่"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"แอป <xliff:g id="NUMBER">%1$d</xliff:g> แอปกำลังใช้แบตเตอรี่"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"แตะเพื่อดูรายละเอียดเกี่ยวกับแบตเตอรี่และปริมาณการใช้อินเทอร์เน็ต"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ใช้ทางลัด"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"การกลับสี"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"การแก้สี"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"ลดความสว่าง"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"หรี่แสงเพิ่มเติม"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว เปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว ปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"กดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 3 วินาทีเพื่อใช้ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ความเป็นส่วนตัวสำหรับเซ็นเซอร์"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ไอคอนแอปพลิเคชัน"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ภาพลักษณ์ของแบรนด์แอปพลิเคชัน"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"ตรวจสอบการตั้งค่าการเข้าถึง"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> จะดูและควบคุมหน้าจอของคุณได้ แตะเพื่อตรวจสอบ"</string>
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 5690798..030a3e5 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Tumatakbo ang app"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Mga app na kumokonsumo ng baterya"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Pag-magnify"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Patakaran sa seguridad ng accessibility"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"Gumagamit ng baterya ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Gumagamit ng baterya ang <xliff:g id="NUMBER">%1$d</xliff:g> (na) app"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"I-tap para sa mga detalye tungkol sa paggamit ng baterya at data"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gamitin ang Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Pag-invert ng Kulay"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Pagwawasto ng Kulay"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Bawasan ang liwanag"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pinindot nang matagal ang volume keys. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pindutin nang matagal ang parehong volume key sa loob ng tatlong segundo para magamit ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacy ng Sensor"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Icon ng application"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Representasyon ng brand ng application"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Tingnan ang mga setting ng pag-access"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"Makikita at makokontrol ng <xliff:g id="SERVICE_NAME">%s</xliff:g> ang iyong screen. I-tap para suriin."</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 008df40..30bf808 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Uygulama çalışıyor"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Pil kullanan uygulamalar"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Büyütme"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Erişilebilirlik güvenlik politikası"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> pil kullanıyor"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> uygulama pil kullanıyor"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Pil ve veri kullanımı ile ilgili ayrıntılar için dokunun"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kısayolu Kullan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Rengi Ters Çevirme"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Renk Düzeltme"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Parlaklığı azalt"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra loş"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kapatıldı."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini kullanmak için her iki ses tuşunu basılı tutun"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensör Gizliliği"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Uygulama simgesi"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Uygulama marka imajı"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Erişim ayarlarını kontrol edin"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g>, ekranınızı görüntüleyip kontrol edebilir. İncelemek için dokunun."</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 86e251b..30a829a 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -300,8 +300,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Працює додаток"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Додатки, що використовують заряд акумулятора"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Збільшення"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Правила щодо безпеки спеціальних можливостей"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> використовує заряд акумулятора"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Додатків, що використовують заряд акумулятора: <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Торкніться, щоб перевірити використання акумулятора й трафік"</string>
@@ -1734,7 +1733,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Використовувати ярлик"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія кольорів"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекція кольорів"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Зменшення яскравості"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Додаткове зменшення яскравості"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> увімкнено."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> вимкнено."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Щоб скористатися службою <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, утримуйте обидві клавіші гучності впродовж трьох секунд"</string>
@@ -2315,8 +2314,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Конфіденційність датчиків"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Значок додатка"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Зображення фірмової символіки додатка"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Перевірте налаштування доступу"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> може переглядати екран вашого пристрою та керувати ним. Натисніть, щоб переглянути."</string>
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 03e683d..c17309b 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ایپ چل رہی ہے"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"ایپس بیٹری خرچ کر رہی ہیں"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"میگنیفکیشن"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"ایکسیسبیلٹی سیکیورٹی کی پالیسی"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> بیٹری کا استعمال کر رہی ہے"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ایپس بیٹری کا استعمال کر رہی ہیں"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"بیٹری اور ڈیٹا استعمال کے بارے میں تفصیلات کے لیے تھپتھپائیں"</string>
@@ -346,10 +345,8 @@
<string name="permdesc_statusBarService" msgid="6652917399085712557">"ایپ کو اسٹیٹس بار بننے کی اجازت دیتا ہے۔"</string>
<string name="permlab_expandStatusBar" msgid="1184232794782141698">"حیثیت بار پھیلائیں/سکیڑیں"</string>
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"ایپ کو اسٹیٹس بار پھیلانے یا سکیڑنے کی اجازت دیتا ہے۔"</string>
- <!-- no translation found for permlab_fullScreenIntent (4310888199502509104) -->
- <skip />
- <!-- no translation found for permdesc_fullScreenIntent (1100721419406643997) -->
- <skip />
+ <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"مقفل آلے پر فُل اسکرین سرگرمیوں کے بطور اطلاعات کو ڈسپلے کریں"</string>
+ <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"ایپ کو مقفل آلے پر فُل اسکرین سرگرمیوں کے بطور اطلاعات کو ڈسپلے کرنے کی اجازت دیتا ہے"</string>
<string name="permlab_install_shortcut" msgid="7451554307502256221">"شارٹ کٹس انسٹال کریں"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"کسی ایپلیکیشن کو صارف کی مداخلت کے بغیر ہوم اسکرین شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"شارٹ کٹس کو اَن انسٹال کریں"</string>
@@ -1692,7 +1689,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"شارٹ کٹ استعمال کریں"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"رنگوں کی تقلیب"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"رنگ کی تصحیح"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"چمک کم کریں"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (3222994553174604132) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آن ہے۔"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آف ہے۔"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> کا استعمال کرنے کے لیے 3 سیکنڈ تک والیوم کی دونوں کلیدوں کو چھوئیں اور دبائے رکھیں"</string>
@@ -2101,8 +2099,7 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"آن اسکرین ایکسیسبیلٹی شارٹ کٹ"</string>
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"آن اسکرین ایکسیسبیلٹی شارٹ کٹ منتخب کنندہ"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ایکسیسبیلٹی کا شارٹ کٹ"</string>
- <!-- no translation found for accessibility_system_action_dismiss_notification_shade (8931637495533770352) -->
- <skip />
+ <string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"اطلاعاتی شیڈ برخاست کریں"</string>
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> کی کیپشن بار۔"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> کو پابند کردہ بکٹ میں رکھ دیا گیا ہے"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
@@ -2240,10 +2237,8 @@
<string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
- <!-- no translation found for window_magnification_prompt_title (4657040468055863672) -->
- <skip />
- <!-- no translation found for window_magnification_prompt_content (3549230303326142349) -->
- <skip />
+ <string name="window_magnification_prompt_title" msgid="4657040468055863672">"اپنی اسکرین کے کسی حصے کو بڑا کریں"</string>
+ <string name="window_magnification_prompt_content" msgid="3549230303326142349">"آپ اپنی فُل اسکرین، کسی مخصوص حصے کو بڑا کر سکتے ہیں یا دونوں اختیارات کے درمیان سوئچ کر سکتے ہیں۔"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ترتیبات میں آن کریں"</string>
<string name="dismiss_action" msgid="1728820550388704784">"برخاست کریں"</string>
<string name="sensor_privacy_start_use_mic_notification_content" msgid="8063355861118105607">"جاری رکھنے کیلئے <xliff:g id="APP">%s</xliff:g><b><b> کو آپ کے آلے کے مائیکروفون تک رسائی درکار ہے۔"</string>
@@ -2252,8 +2247,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"سینسر کی رازداری"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ایپلیکیشن کا آئیکن"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ایپلیکیشن کی برانڈنگ تصویر"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"رسائی کی ترتیبات چیک کریں"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> آپ کی اسکرین کو دیکھ اور کنٹرول کر سکتی ہیں۔ جائزے کے لیے تھپتھپائیں۔"</string>
</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index b601161..f302d33 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Ilova faol"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Batareya quvvatini sarflayotgan ilovalar"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Kattalashtirish"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Maxsus imkoniyatlar xavfsizlik siyosati"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi batareya quvvatini sarflamoqda"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ta ilova batareya quvvatini sarflamoqda"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Batareya va trafik sarfi tafsilotlari uchun ustiga bosing"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tezkor ishga tushirishdan foydalanish"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ranglarni akslantirish"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Rangni tuzatish"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Yorqinlikni pasaytirish"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Juda xira"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> yoqildi."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> faolsizlantirildi."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmatidan foydalanish uchun ikkala ovoz balandligi tugmalarini uzoq bosib turing"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorlar maxfiyligi"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ilova belgisi"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Ilova brendining rasmi"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Kirish sozlamalarini tekshiring"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ekraningizni koʻrishi va boshqarishi mumkin. Tekshirish uchun bosing."</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index ba0aa4b..421e1e7 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Ứng dụng đang chạy"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Các ứng dụng tiêu thụ pin"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Phóng to"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Chính sách bảo mật của tính năng Hỗ trợ tiếp cận"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang sử dụng pin"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ứng dụng đang sử dụng pin"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Nhấn để biết chi tiết về mức sử dụng dữ liệu và pin"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sử dụng phím tắt"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Đảo màu"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Chỉnh màu"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Giảm độ sáng"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Giảm độ sáng hơn nữa"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã bật."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã tắt."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Nhấn và giữ đồng thời cả hai phím âm lượng trong 3 giây để sử dụng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2085,7 +2084,7 @@
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> tệp</item>
<item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> + <xliff:g id="COUNT_1">%d</xliff:g> tệp</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Không có gợi ý về người để chia sẻ"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"Không có gợi ý nào về người mà bạn có thể chia sẻ"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"Danh sách ứng dụng"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Ứng dụng này chưa được cấp quyền ghi âm nhưng vẫn có thể ghi âm thông qua thiết bị USB này."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Màn hình chính"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Quyền riêng tư khi sử dụng cảm biến"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Biểu tượng ứng dụng"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Hình ảnh thương hiệu của ứng dụng"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Kiểm tra chế độ cài đặt quyền truy cập"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> có thể xem và điều khiển màn hình của bạn. Nhấn để xem lại."</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index d2aa30d..d7c64e2 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"应用正在运行中"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"消耗电量的应用"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"放大功能"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"无障碍服务安全政策"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>正在消耗电量"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 个应用正在消耗电量"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"点按即可详细了解电量和流量消耗情况"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用快捷方式"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"颜色反转"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"调低亮度"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"超暗"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已开启。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已关闭。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"同时按住两个音量键 3 秒钟即可使用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"传感器隐私权"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"应用图标"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"应用品牌图片"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"查看权限设置"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g>可以查看和控制您的屏幕。点按即可查看。"</string>
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index fd0849a..bc89bed 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"應用程式正在執行"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"耗用電量的應用程式"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"放大"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"無障礙工具安全性政策"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在使用電量"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 個應用程式正在使用電量"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"輕按即可查看電池和數據用量詳情"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用快速鍵"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"色彩反轉"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"調低亮度"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"超暗"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已開啟。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已關閉。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"㩒住兩個音量鍵 3 秒就可以用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"感應器私隱"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"應用程式圖示"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"應用程式品牌形象"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"檢查存取權設定"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> 可以查看及控制您的螢幕。輕按即可查看。"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index d541d58..f728be8 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"應用程式執行中"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"正在耗用電量的應用程式"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"放大"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"無障礙工具安全性政策"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在耗用電量"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 個應用程式正在耗用電量"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"輕觸即可查看電池和數據用量詳情"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用捷徑"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"色彩反轉"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"調低亮度"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"超暗"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已開啟。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已關閉。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"同時按住調低及調高音量鍵三秒即可使用「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"感應器隱私權"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"應用程式圖示"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"應用程式品牌圖片"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"查看存取權設定"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"「<xliff:g id="SERVICE_NAME">%s</xliff:g>」可以查看及控管你的螢幕。輕觸即可查看。"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index a577bca..dc15edc 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -294,8 +294,7 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Uhlelo loksuebenza olusebenzayo"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"Izinhlelo zokusebenza ezidla ibhethri"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ukukhuliswa"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (3350443906017624270) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="3350443906017624270">"Inqubomgomo yokuvikeleka kokufinyeleleka"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> isebenzisa ibhethri"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> izinhlelo zokusebenza zisebenzisa ibhethri"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Thepha ngemininingwane ekusetshenzisweni kwebhethri nedatha"</string>
@@ -1690,7 +1689,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sebenzisa isinqamuleli"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ukuguqulwa kombala"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Ukulungiswa kombala"</string>
- <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Nciphisa ukukhanya"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ukufiphaza okwengeziwe"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivuliwe."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivaliwe."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Cindezela uphinde ubambe bobabili okhiye bevolumu ngamasekhondi amathathu ukuze usebenzise i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -2247,8 +2246,6 @@
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Ubumfihlo Benzwa"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Isithonjana sohlelo lokusebenza"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Isithombe sokubhrenda i-application"</string>
- <!-- no translation found for view_and_control_notification_title (4300765399209912240) -->
- <skip />
- <!-- no translation found for view_and_control_notification_content (8003766498562604034) -->
- <skip />
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"Hlola amasethingi wokufinyelela"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"I-<xliff:g id="SERVICE_NAME">%s</xliff:g> ingakwazi ukubuka nokulawula isikrini sakho. Thepha ukuze ubuyekeze."</string>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4b15e01..eea3799 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6070,6 +6070,8 @@
<attr name="color" />
<!-- Alpha multiplier applied to the base color. -->
<attr name="alpha" />
+ <!-- Perceptual luminance applied to the base color. From 0 to 100. -->
+ <attr name="lStar" format="float" />
</declare-styleable>
<!-- Drawable used to render according to the animation scale. Esp. when it is 0 due to battery
@@ -9535,4 +9537,12 @@
<attr name="iconfactoryIconSize" format="dimension"/>
<attr name="iconfactoryBadgeSize" format="dimension"/>
+ <!-- Perceptual luminance of a color, in accessibility friendly color space. From 0 to 100. -->
+ <attr name="lStar" format="float"/>
+
+ <declare-styleable name="DisplayHasherService">
+ <!-- The throttle duration for display hash requests
+ @hide @SystemApi -->
+ <attr name="throttleDurationMillis" format="integer" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index dc4f52e..986bb82 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1595,6 +1595,20 @@
<enum name="always" value="1" />
</attr>
+ <!-- Enable hardware memory tagging (ARM MTE) in this process.
+ When enabled, heap memory bugs like use-after-free and buffer overlow
+ are detected and result in an immediate ("sync" mode) or delayed ("async"
+ mode) crash instead of a silent memory corruption. Sync mode, while slower,
+ provides enhanced bug reports including stack traces at the time of allocation
+ and deallocation of memory, similar to AddressSanitizer.
+
+ See the <a href="https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/enhancing-memory-safety">ARM announcement</a>
+ for more details.
+
+ <p>This attribute can be applied to a
+ {@link android.R.styleable#AndroidManifestProcess process} tag, or to an
+ {@link android.R.styleable#AndroidManifestApplication application} tag (to supply
+ a default setting for all application components). -->
<attr name="memtagMode">
<enum name="default" value="-1" />
<enum name="off" value="0" />
@@ -1898,7 +1912,9 @@
<attr name="memtagMode" />
- <attr name="nativeHeapZeroInit" format="boolean" />
+ <!-- If {@code true} enables automatic zero initialization of all native heap
+ allocations. -->
+ <attr name="nativeHeapZeroInitialized" format="boolean" />
<!-- @hide no longer used, kept to preserve padding -->
<attr name="allowAutoRevokePermissionsExemption" format="boolean" />
@@ -2486,7 +2502,7 @@
<attr name="process" />
<attr name="gwpAsanMode" />
<attr name="memtagMode" />
- <attr name="nativeHeapZeroInit" />
+ <attr name="nativeHeapZeroInitialized" />
</declare-styleable>
<!-- The <code>deny-permission</code> tag specifies that a permission is to be denied
diff --git a/core/res/res/values/colors_leanback.xml b/core/res/res/values/colors_leanback.xml
index df0be03..1b0fad8 100644
--- a/core/res/res/values/colors_leanback.xml
+++ b/core/res/res/values/colors_leanback.xml
@@ -16,15 +16,11 @@
<!-- Colors specific to Leanback themes. -->
<resources>
- <color name="background_leanback_dark">#ff324248</color>
- <color name="background_leanback_light">#ffeeeeee</color>
+ <color name="background_leanback_dark">#FF1F232B</color>
<color name="primary_text_leanback_dark">#cceeeeee</color>
<color name="secondary_text_leanback_dark">#99eeeeee</color>
- <color name="primary_text_leanback_light">#cc222222</color>
- <color name="secondary_text_leanback_light">#99222222</color>
-
<color name="primary_text_leanback_formwizard_default_dark">#ffeeeeee</color>
<color name="btn_leanback_focused">#E8EAED</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8df3221..842f12d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3561,10 +3561,6 @@
<!-- True if assistant app should be pinned via Pinner Service -->
<bool name="config_pinnerAssistantApp">false</bool>
- <!-- List of files pinned by the Pinner Service with the JIT Zygote boot image b/119800099 -->
- <string-array translatable="false" name="config_jitzygoteBootImagePinnerServiceFiles">
- </string-array>
-
<!-- Number of days preloaded file cache should be preserved on a device before it can be
deleted -->
<integer name="config_keepPreloadsMinDays">7</integer>
@@ -3732,6 +3728,13 @@
-->
<string name="config_defaultWellbeingPackage" translatable="false"></string>
+ <!-- The package name for the companion provider app.
+ This package must be trusted, as it has the permissions to associate apps with devices
+ without a UI prompt.
+ Example: "com.google.android.gms"
+ -->
+ <string name="config_companionProviderPackage" translatable="false"></string>
+
<!-- The component name for the default system attention service.
This service must be trusted, as it can be activated without explicit consent of the user.
See android.attention.AttentionManagerService.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index afbbe46..cf5b4e1 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -47,10 +47,6 @@
<dimen name="status_bar_height_landscape">@dimen/status_bar_height_portrait</dimen>
<!-- Height of area above QQS where battery/time go -->
<dimen name="quick_qs_offset_height">48dp</dimen>
- <!-- Total height of QQS (quick_qs_offset_height + 128) -->
- <dimen name="quick_qs_total_height">176dp</dimen>
- <!-- Total height of QQS with two rows to fit media player (quick_qs_offset_height + 176) -->
- <dimen name="quick_qs_total_height_with_media">224dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_height">48dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
diff --git a/core/res/res/values/dimens_leanback.xml b/core/res/res/values/dimens_leanback.xml
index 3ab2196..ecda735 100644
--- a/core/res/res/values/dimens_leanback.xml
+++ b/core/res/res/values/dimens_leanback.xml
@@ -89,4 +89,6 @@
<dimen name="leanback_button_radius">55dp</dimen>
<dimen name="leanback_button_padding_horizontal">22dp</dimen>
<dimen name="leanback_button_padding_vertical">11dp</dimen>
+
+ <dimen name="leanback_dialog_corner_radius">8dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 16feb4f..aea9d60 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3059,7 +3059,7 @@
<public name="fontProviderSystemFontFamily" />
<public name="hand_second" />
<public name="memtagMode" />
- <public name="nativeHeapZeroInit" />
+ <public name="nativeHeapZeroInitialized" />
<!-- @hide @SystemApi -->
<public name="hotwordDetectionService" />
<public name="previewLayout" />
@@ -3095,6 +3095,9 @@
<public name="requestOptimizedExternalStorageAccess" />
<!-- @hide @SystemApi -->
<public name="playHomeTransitionSound" />
+ <public name="lStar" />
+ <!-- @hide @SystemApi -->
+ <public name="throttleDurationMillis" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c63a02b..77ef15b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -745,7 +745,7 @@
<!-- Text shown when viewing channel settings for notifications related to accessibility
security policy. [CHAR_LIMIT=NONE]-->
- <string name="notification_channel_accessibility_security_policy">Accessibility security policy</string>
+ <string name="notification_channel_accessibility_security_policy">Accessibility usage</string>
<!-- Label for foreground service notification when one app is running.
[CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6826789589341671842] -->
@@ -1579,15 +1579,21 @@
<string name="screen_lock_dialog_default_subtitle">Enter your device credential to continue</string>
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
- <string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string>
+ <string name="fingerprint_acquired_partial">Partial fingerprint detected</string>
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
<string name="fingerprint_acquired_insufficient">Couldn\'t process fingerprint. Please try again.</string>
<!-- Message shown during fingerprint acquisision when the fingerprint sensor needs cleaning -->
- <string name="fingerprint_acquired_imager_dirty">Fingerprint sensor is dirty. Please clean and try again.</string>
+ <string name="fingerprint_acquired_imager_dirty">Clean the sensor</string>
<!-- Message shown during fingerprint acquisision when the user removes their finger from the sensor too quickly -->
- <string name="fingerprint_acquired_too_fast">Finger moved too fast. Please try again.</string>
+ <string name="fingerprint_acquired_too_fast">Finger moved too fast</string>
<!-- Message shown during fingerprint acquisision when the user moves their finger too slowly -->
<string name="fingerprint_acquired_too_slow">Finger moved too slow. Please try again.</string>
+ <!-- Message shown during fingerprint acquisition when the fingerprint was already enrolled.[CHAR LIMIT=50] -->
+ <string name="fingerprint_acquired_already_enrolled">Try another fingerprint</string>
+ <!-- Message shown during fingerprint acquisition when fingerprint sensor detected too much light.[CHAR LIMIT=50] -->
+ <string name="fingerprint_acquired_too_bright">Too bright</string>
+ <!-- Message shown during fingerprint acquisition when a fingerprint must be adjusted.[CHAR LIMIT=50] -->
+ <string name="fingerprint_acquired_try_adjusting">Try adjusting</string>
<!-- Array containing custom messages shown during fingerprint acquisision from vendor. Vendor is expected to add and translate these strings -->
<string-array name="fingerprint_acquired_vendor">
</string-array>
@@ -1602,7 +1608,7 @@
<!-- Error message shown when the fingerprint hardware can't be accessed -->
<string name="fingerprint_error_hw_not_available">Fingerprint hardware not available.</string>
<!-- Error message shown when the fingerprint hardware has run out of room for storing fingerprints -->
- <string name="fingerprint_error_no_space">Fingerprint can\'t be stored. Please remove an existing fingerprint.</string>
+ <string name="fingerprint_error_no_space">Can\u2019t set up fingerprint</string>
<!-- Error message shown when the fingerprint hardware timer has expired and the user needs to restart the operation. -->
<string name="fingerprint_error_timeout">Fingerprint time out reached. Try again.</string>
<!-- Generic error message shown when the fingerprint operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user-->
@@ -1656,6 +1662,14 @@
<string name="face_recalibrate_notification_title">Re-enroll your face</string>
<!-- Notification content shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
<string name="face_recalibrate_notification_content">To improve recognition, please re-enroll your face</string>
+ <!-- Title of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
+ <string name="face_setup_notification_title">Set up face unlock</string>
+ <!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
+ <string name="face_setup_notification_content">Unlock your phone by looking at it</string>
+ <!-- Title of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_setup_notification_title">Set up more ways to unlock</string>
+ <!-- Contents of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_setup_notification_content">Tap to add a fingerprint</string>
<!-- Message shown during face acquisition when the face cannot be recognized [CHAR LIMIT=50] -->
<string name="face_acquired_insufficient">Couldn\u2019t capture accurate face data. Try again.</string>
@@ -5654,15 +5668,11 @@
<!-- Button text. This button turns on a user's work profile so they can access their work apps and data. [CHAR LIMIT=NONE] -->
<string name="resolver_switch_on_work">Tap to turn on</string>
- <!-- Error message. This text lets the user know that their current work apps don't support the specific content that they're trying to share. [CHAR LIMIT=NONE] -->
- <string name="resolver_no_work_apps_available_share">No work apps can support this content</string>
- <!-- Error message. This text lets the user know that their current work apps can't open this specific content. [CHAR LIMIT=NONE] -->
- <string name="resolver_no_work_apps_available_resolve">No work apps can open this content</string>
+ <!-- Error message. This text lets the user know that their current work apps don't support the specific content. [CHAR LIMIT=NONE] -->
+ <string name="resolver_no_work_apps_available">No work apps</string>
- <!-- Error message. This text lets the user know that their current personal apps don't support the specific content that they're trying to share. [CHAR LIMIT=NONE] -->
- <string name="resolver_no_personal_apps_available_share">No personal apps can support this content</string>
- <!-- Error message. This text lets the user know that their current personal apps can't open this specific content. [CHAR LIMIT=NONE] -->
- <string name="resolver_no_personal_apps_available_resolve">No personal apps can open this content</string>
+ <!-- Error message. This text lets the user know that their current personal apps don't support the specific content. [CHAR LIMIT=NONE] -->
+ <string name="resolver_no_personal_apps_available">No personal apps</string>
<!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
<string name="miniresolver_open_in_personal">Open in <xliff:g id="app" example="YouTube">%s</xliff:g> in personal profile?</string>
@@ -5918,9 +5928,9 @@
<!-- Window magnification prompt related string. -->
<!-- Notification title to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
- <string name="window_magnification_prompt_title">Magnify part of your screen</string>
+ <string name="window_magnification_prompt_title">New magnification settings</string>
<!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=NONE] -->
- <string name="window_magnification_prompt_content">You can now magnify your full screen, a specific area, or switch between both options.</string>
+ <string name="window_magnification_prompt_content">You can now magnify part of your screen</string>
<!-- Notification action to bring the user to magnification settings page. [CHAR LIMIT=50] -->
<string name="turn_on_magnification_settings_action">Turn on in Settings</string>
<!-- Notification action to dismiss. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 01374b1..beed9a3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1716,8 +1716,6 @@
<java-symbol type="dimen" name="status_bar_height" />
<java-symbol type="dimen" name="display_cutout_touchable_region_size" />
<java-symbol type="dimen" name="quick_qs_offset_height" />
- <java-symbol type="dimen" name="quick_qs_total_height" />
- <java-symbol type="dimen" name="quick_qs_total_height_with_media" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
<java-symbol type="drawable" name="ic_jog_dial_unlock" />
@@ -3148,7 +3146,6 @@
<java-symbol type="bool" name="config_pinnerCameraApp" />
<java-symbol type="bool" name="config_pinnerHomeApp" />
<java-symbol type="bool" name="config_pinnerAssistantApp" />
- <java-symbol type="array" name="config_jitzygoteBootImagePinnerServiceFiles" />
<java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
@@ -3536,6 +3533,7 @@
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="notification_channel_accessibility_magnification" />
<java-symbol type="string" name="notification_channel_accessibility_security_policy" />
+ <java-symbol type="string" name="config_companionProviderPackage" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
@@ -4101,10 +4099,8 @@
<java-symbol type="string" name="resolver_cant_access_work_apps_explanation" />
<java-symbol type="string" name="resolver_cant_access_personal_apps_explanation" />
<java-symbol type="string" name="resolver_turn_on_work_apps" />
- <java-symbol type="string" name="resolver_no_work_apps_available_share" />
- <java-symbol type="string" name="resolver_no_work_apps_available_resolve" />
- <java-symbol type="string" name="resolver_no_personal_apps_available_share" />
- <java-symbol type="string" name="resolver_no_personal_apps_available_resolve" />
+ <java-symbol type="string" name="resolver_no_work_apps_available" />
+ <java-symbol type="string" name="resolver_no_personal_apps_available" />
<java-symbol type="string" name="resolver_switch_on_work" />
<java-symbol type="drawable" name="ic_work_apps_off" />
<java-symbol type="drawable" name="ic_sharing_disabled" />
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index efe5826..0be9e9800 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -15,31 +15,21 @@
-->
<resources>
<style name="Theme.Leanback.Dialog" parent="Theme.Material.BaseDialog">
- <item name="colorBackground">@color/background_leanback_dark</item>
- <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
- <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
- <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
- <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
- <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
- <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
- <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item>
- <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
- </style>
-
- <style name="Theme.Leanback.Light.Dialog" parent="Theme.Material.Light.BaseDialog">
- <item name="colorBackground">@color/background_leanback_dark</item>
- <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
- <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
- <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
- <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
- <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
- <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
+ <item name="colorBackground">@color/background_leanback_dark</item>
+ <item name="dialogCornerRadius">@dimen/leanback_dialog_corner_radius</item>
+ <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
+ <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
+ <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
+ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
+ <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
+ <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
<item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item>
- <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
+ <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
</style>
<style name="Theme.Leanback.Settings.Dialog" parent="Theme.Material.Settings.BaseDialog">
<item name="colorBackground">@color/background_leanback_dark</item>
+ <item name="dialogCornerRadius">@dimen/leanback_dialog_corner_radius</item>
<item name="textColorPrimary">@color/primary_text_leanback_dark</item>
<item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
<item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
@@ -51,34 +41,25 @@
</style>
<style name="Theme.Leanback.Dialog.Alert" parent="Theme.Material.Dialog.BaseAlert">
- <item name="colorBackground">@color/background_leanback_dark</item>
- <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
- <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
- <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
- <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
- <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
- <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
- <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item>
- <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
- </style>
-
- <style name="Theme.Leanback.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.BaseAlert">
- <item name="colorBackground">@color/background_leanback_light</item>
- <item name="textColorPrimary">@color/primary_text_leanback_light</item>
- <item name="textColorSecondary">@color/secondary_text_leanback_light</item>
- <item name="alertDialogStyle">@style/AlertDialog.Leanback.Light</item>
- <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
- <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
- <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
+ <item name="colorBackground">@color/background_leanback_dark</item>
+ <item name="dialogCornerRadius">@dimen/leanback_dialog_corner_radius</item>
+ <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
+ <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
+ <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
+ <item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
+ <item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
+ <item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
<item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item>
- <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
+ <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
</style>
- <style name="Theme.Leanback.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.BaseAlert">
- <item name="colorBackground">@color/background_leanback_light</item>
- <item name="textColorPrimary">@color/primary_text_leanback_light</item>
- <item name="textColorSecondary">@color/secondary_text_leanback_light</item>
- <item name="alertDialogStyle">@style/AlertDialog.Leanback.Light</item>
+ <style name="Theme.Leanback.Settings.Dialog.Alert"
+ parent="Theme.Material.Settings.Dialog.BaseAlert">
+ <item name="colorBackground">@color/background_leanback_dark</item>
+ <item name="dialogCornerRadius">@dimen/leanback_dialog_corner_radius</item>
+ <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
+ <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
+ <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
<item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
<item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
<item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
@@ -144,9 +125,9 @@
<!-- Icon sizes -->
<item name="iconfactoryIconSize">@dimen/resolver_icon_size</item>
<item name="iconfactoryBadgeSize">@dimen/resolver_badge_size</item>
- </style>
+ </style>
<!-- @hide Special theme for the default system Activity-based Alert dialogs. -->
- <style name="Theme.Leanback.Dialog.Confirmation" parent="Theme.DeviceDefault.Dialog.Alert" />
+ <style name="Theme.Leanback.Dialog.Confirmation" parent="Theme.DeviceDefault.Dialog.Alert"/>
</resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
index 6d9e2ea5..56c685a 100644
--- a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
@@ -42,8 +42,8 @@
public void setUp() throws Exception {
// Remove all documents from any instances that may have been created in the tests.
Objects.requireNonNull(mAppSearch);
- AppSearchManager.SearchContext searchContext = new AppSearchManager.SearchContext.Builder()
- .setDatabaseName("testDb").build();
+ AppSearchManager.SearchContext searchContext =
+ new AppSearchManager.SearchContext.Builder("testDb").build();
CompletableFuture<AppSearchResult<AppSearchSession>> future = new CompletableFuture<>();
mAppSearch.createSearchSession(searchContext, mExecutor, future::complete);
mSearchSession = future.get().getResultValue();
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index b517428..01e240a 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -86,7 +86,7 @@
final AssetFileDescriptor afd = new AssetFileDescriptor(
new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null);
- when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any(), any())).thenReturn(
+ when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(
afd);
}
diff --git a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
index ffe93bc..21eb44a 100644
--- a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
@@ -45,7 +45,7 @@
final Set<String> categorySet = new ArraySet<>();
categorySet.add(category);
final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW);
- final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(id)
+ final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(/*packageName=*/"", id)
.setActivity(activity)
.setLongLabel(id)
.setIconResName(shortcutIconResName)
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index 40fc00a..2521f75 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
import android.platform.test.annotations.Presubmit;
import android.util.Range;
@@ -98,6 +99,16 @@
}
@Test
+ public void testGetDefaultBraking_returnsFirstSupportedBraking() {
+ assertEquals(Braking.NONE, new InfoBuilder().build().getDefaultBraking());
+ assertEquals(Braking.CLAB,
+ new InfoBuilder()
+ .setSupportedBraking(Braking.NONE, Braking.CLAB)
+ .build()
+ .getDefaultBraking());
+ }
+
+ @Test
public void testGetFrequencyRange_invalidFrequencyMappingReturnsEmptyRange() {
// Invalid, contains NaN values or empty array.
assertEquals(Range.create(0f, 0f), new InfoBuilder().build().getFrequencyRange());
@@ -318,6 +329,7 @@
private int mId = 0;
private int mCapabilities = 0;
private int[] mSupportedEffects = null;
+ private int[] mSupportedBraking = null;
private int[] mSupportedPrimitives = null;
private float mQFactor = Float.NaN;
private VibratorInfo.FrequencyMapping mFrequencyMapping = EMPTY_FREQUENCY_MAPPING;
@@ -337,6 +349,11 @@
return this;
}
+ public InfoBuilder setSupportedBraking(int... supportedBraking) {
+ mSupportedBraking = supportedBraking;
+ return this;
+ }
+
public InfoBuilder setSupportedPrimitives(int... supportedPrimitives) {
mSupportedPrimitives = supportedPrimitives;
return this;
@@ -353,8 +370,8 @@
}
public VibratorInfo build() {
- return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives,
- mQFactor, mFrequencyMapping);
+ return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
+ mSupportedPrimitives, mQFactor, mFrequencyMapping);
}
}
}
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index 4212ef2..97e66c4 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -33,6 +33,7 @@
import android.util.MemoryIntArray;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
@@ -73,7 +74,8 @@
Settings.Config.clearProviderForTest();
MockitoAnnotations.initMocks(this);
when(mMockContentProvider.getIContentProvider()).thenReturn(mMockIContentProvider);
- mMockContentResolver = new MockContentResolver();
+ mMockContentResolver = new MockContentResolver(InstrumentationRegistry
+ .getInstrumentation().getContext());
mMockContentResolver.addProvider(DeviceConfig.CONTENT_URI.getAuthority(),
mMockContentProvider);
mCacheGenerationStore = new MemoryIntArray(1);
@@ -82,10 +84,10 @@
// Stores keyValues for a given prefix and increments the generation. (Note that this
// increments the generation no matter what, it doesn't pay attention to if anything
// actually changed).
- when(mMockIContentProvider.call(any(), any(), eq(DeviceConfig.CONTENT_URI.getAuthority()),
+ when(mMockIContentProvider.call(any(), eq(DeviceConfig.CONTENT_URI.getAuthority()),
eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
any(), any(Bundle.class))).thenAnswer(invocationOnMock -> {
- Bundle incomingBundle = invocationOnMock.getArgument(5);
+ Bundle incomingBundle = invocationOnMock.getArgument(4);
HashMap<String, String> keyValues =
(HashMap<String, String>) incomingBundle.getSerializable(
Settings.CALL_METHOD_FLAGS_KEY);
@@ -101,10 +103,10 @@
// Returns the keyValues corresponding to a namespace, or an empty map if the namespace
// doesn't have anything stored for it. Returns the generation key if the caller asked
// for one.
- when(mMockIContentProvider.call(any(), any(), eq(DeviceConfig.CONTENT_URI.getAuthority()),
+ when(mMockIContentProvider.call(any(), eq(DeviceConfig.CONTENT_URI.getAuthority()),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class))).thenAnswer(invocationOnMock -> {
- Bundle incomingBundle = invocationOnMock.getArgument(5);
+ Bundle incomingBundle = invocationOnMock.getArgument(4);
String prefix = incomingBundle.getString(Settings.CALL_METHOD_PREFIX_KEY);
@@ -132,14 +134,14 @@
HashMap<String, String> keyValues = new HashMap<>();
keyValues.put("a", "b");
Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
- verify(mMockIContentProvider).call(any(), any(), any(),
+ verify(mMockIContentProvider).call(any(), any(),
eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
any(), any(Bundle.class));
Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
NAMESPACE,
Collections.emptyList());
- verify(mMockIContentProvider).call(any(), any(), any(),
+ verify(mMockIContentProvider).call(any(), any(),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class));
assertThat(returnedValues).containsExactlyEntriesIn(keyValues);
@@ -152,13 +154,13 @@
// Modify the value to invalidate the cache.
keyValues.put("a", "c");
Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
- verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ verify(mMockIContentProvider, times(2)).call(any(), any(),
eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
any(), any(Bundle.class));
Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver,
NAMESPACE, Collections.emptyList());
- verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ verify(mMockIContentProvider, times(2)).call(any(), any(),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class));
assertThat(returnedValues2).containsExactlyEntriesIn(keyValues);
@@ -174,7 +176,7 @@
HashMap<String, String> keyValues = new HashMap<>();
keyValues.put("a", "b");
Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
- verify(mMockIContentProvider).call(any(), any(), any(),
+ verify(mMockIContentProvider).call(any(), any(),
eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
any(), any(Bundle.class));
@@ -182,14 +184,14 @@
keyValues2.put("c", "d");
keyValues2.put("e", "f");
Settings.Config.setStrings(mMockContentResolver, NAMESPACE2, keyValues2);
- verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ verify(mMockIContentProvider, times(2)).call(any(), any(),
eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
any(), any(Bundle.class));
Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
NAMESPACE,
Collections.emptyList());
- verify(mMockIContentProvider).call(any(), any(), any(),
+ verify(mMockIContentProvider).call(any(), any(),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class));
assertThat(returnedValues).containsExactlyEntriesIn(keyValues);
@@ -197,7 +199,7 @@
Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver,
NAMESPACE2,
Collections.emptyList());
- verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ verify(mMockIContentProvider, times(2)).call(any(), any(),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class));
assertThat(returnedValues2).containsExactlyEntriesIn(keyValues2);
@@ -218,7 +220,7 @@
Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
NAMESPACE,
Collections.emptyList());
- verify(mMockIContentProvider).call(any(), any(), any(),
+ verify(mMockIContentProvider).call(any(), any(),
eq(Settings.CALL_METHOD_LIST_CONFIG),
any(), any(Bundle.class));
assertThat(returnedValues).isEmpty();
diff --git a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
index 5f640be..4496983 100644
--- a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
+++ b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
@@ -18,13 +18,13 @@
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;
-import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract.Path;
@@ -93,14 +93,12 @@
}
@Override
- protected int enforceReadPermissionInner(Uri uri, String callingPkg,
- @Nullable String callingFeatureId, IBinder callerToken) {
+ protected int enforceReadPermissionInner(Uri uri, AttributionSource attributionSource) {
return AppOpsManager.MODE_ALLOWED;
}
@Override
- protected int enforceWritePermissionInner(Uri uri, String callingPkg,
- @Nullable String callingFeatureId, IBinder callerToken) {
+ protected int enforceWritePermissionInner(Uri uri, AttributionSource attributionSource) {
return AppOpsManager.MODE_ALLOWED;
}
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 47556c3..34a1fd8 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -97,7 +97,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// test if setVisibility can show IME
- mImeConsumer.onWindowFocusGained();
+ mImeConsumer.onWindowFocusGained(true);
mController.show(WindowInsets.Type.ime(), true /* fromIme */);
mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -116,7 +116,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// Request IME visible before control is available.
- mImeConsumer.onWindowFocusGained();
+ mImeConsumer.onWindowFocusGained(true);
mController.show(WindowInsets.Type.ime(), true /* fromIme */);
// set control and verify visibility is applied.
@@ -132,24 +132,58 @@
}
@Test
- public void testImeGetAndClearSkipAnimationOnce() {
+ public void testImeGetAndClearSkipAnimationOnce_expectSkip() {
+ // Expect IME animation will skipped when the IME is visible at first place.
+ verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, true /* hasViewFocus */,
+ true /* expectSkipAnim */);
+ }
+
+ @Test
+ public void testImeGetAndClearSkipAnimationOnce_expectNoSkip() {
+ // Expect IME animation will not skipped if previously no view focused when gained the
+ // window focus and requesting the IME visible next time.
+ verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, false /* hasViewFocus */,
+ false /* expectSkipAnim */);
+ }
+
+ private void verifyImeGetAndClearSkipAnimationOnce(boolean hasWindowFocus, boolean hasViewFocus,
+ boolean expectSkipAnim) {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// Request IME visible before control is available.
- mImeConsumer.onWindowFocusGained();
- mController.show(WindowInsets.Type.ime(), true /* fromIme */);
+ mImeConsumer.onWindowFocusGained(hasWindowFocus);
+ final boolean imeVisible = hasWindowFocus && hasViewFocus;
+ if (imeVisible) {
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */);
+ }
// set control and verify visibility is applied.
InsetsSourceControl control = Mockito.spy(
new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE));
// Simulate IME source control set this flag when the target has starting window.
control.setSkipAnimationOnce(true);
- mController.onControlsChanged(new InsetsSourceControl[] { control });
- // Verify IME show animation should be triggered when control becomes available and
- // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
- verify(control).getAndClearSkipAnimationOnce();
- verify(mController).applyAnimation(
- eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
- eq(true) /* skipAnim */);
+
+ if (imeVisible) {
+ // Verify IME applyAnimation should be triggered when control becomes available,
+ // and expect skip animation state after getAndClearSkipAnimationOnce invoked.
+ mController.onControlsChanged(new InsetsSourceControl[]{ control });
+ verify(control).getAndClearSkipAnimationOnce();
+ verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
+ eq(true) /* show */, eq(false) /* fromIme */,
+ eq(expectSkipAnim) /* skipAnim */);
+ }
+
+ // If previously hasViewFocus is false, verify when requesting the IME visible next
+ // time will not skip animation.
+ if (!hasViewFocus) {
+ mController.show(WindowInsets.Type.ime(), true);
+ mController.onControlsChanged(new InsetsSourceControl[]{ control });
+ // Verify IME show animation should be triggered when control becomes available and
+ // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
+ verify(control).getAndClearSkipAnimationOnce();
+ verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
+ eq(true) /* show */, eq(true) /* fromIme */,
+ eq(false) /* skipAnim */);
+ }
});
}
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 4390546..6301f32 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -234,7 +234,7 @@
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
+ mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
// since there is no focused view, forcefully make IME visible.
mController.show(Type.ime(), true /* fromIme */);
mController.show(Type.all());
@@ -260,7 +260,7 @@
InsetsSourceControl ime = createControl(ITYPE_IME);
mController.onControlsChanged(new InsetsSourceControl[] { ime });
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
+ mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
mController.show(Type.ime(), true /* fromIme */);
mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
index f3a6f9e..22c71b52 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
@@ -32,7 +32,9 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.ICancellationSignal;
+import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
@@ -45,6 +47,8 @@
* Tests of {@link ScrollCaptureConnection}.
*/
@SuppressWarnings("UnnecessaryLocalVariable")
+@Presubmit
+@SmallTest
@RunWith(AndroidJUnit4.class)
public class ScrollCaptureConnectionTest {
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
index cc229e1..dc43204 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
@@ -32,8 +32,10 @@
import android.graphics.Rect;
import android.os.CancellationSignal;
import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
@@ -47,8 +49,10 @@
import java.util.function.Consumer;
/**
- * Tests of {@link ScrollCaptureTargetSelector}.
+ * Tests of {@link ScrollCaptureSearchResults}.
*/
+@Presubmit
+@SmallTest
@RunWith(AndroidJUnit4.class)
public class ScrollCaptureSearchResultsTest {
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
index 67614bb..e6a25d0 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
@@ -236,12 +236,13 @@
@Test
public void testMergeEvent_typeViewTextChanged() {
final ContentCaptureEvent event = new ContentCaptureEvent(42, TYPE_VIEW_TEXT_CHANGED)
- .setText("test");
+ .setText("test", false);
final ContentCaptureEvent event2 = new ContentCaptureEvent(43, TYPE_VIEW_TEXT_CHANGED)
- .setText("empty");
+ .setText("empty", true);
event.mergeEvent(event2);
assertThat(event.getText()).isEqualTo(event2.getText());
+ assertThat(event.getTextHasComposingSpan()).isEqualTo(event2.getTextHasComposingSpan());
}
@Test
@@ -282,16 +283,18 @@
@Test
public void testMergeEvent_differentEventTypes() {
final ContentCaptureEvent event = new ContentCaptureEvent(42, TYPE_VIEW_DISAPPEARED)
- .setText("test").setAutofillId(new AutofillId(1));
+ .setText("test", false).setAutofillId(new AutofillId(1));
final ContentCaptureEvent event2 = new ContentCaptureEvent(17, TYPE_VIEW_TEXT_CHANGED)
- .setText("empty").setAutofillId(new AutofillId(2));
+ .setText("empty", true).setAutofillId(new AutofillId(2));
event.mergeEvent(event2);
assertThat(event.getText()).isEqualTo("test");
+ assertThat(event.getTextHasComposingSpan()).isFalse();
assertThat(event.getId()).isEqualTo(new AutofillId(1));
event2.mergeEvent(event);
assertThat(event2.getText()).isEqualTo("empty");
+ assertThat(event2.getTextHasComposingSpan()).isTrue();
assertThat(event2.getId()).isEqualTo(new AutofillId(2));
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 80d47a9..1633d28 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -1660,7 +1660,7 @@
onView(withText(R.string.resolver_work_tab)).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_no_work_apps_available_share))
+ onView(withText(R.string.resolver_no_work_apps_available))
.check(matches(isDisplayed()));
}
@@ -1711,7 +1711,7 @@
onView(withText(R.string.resolver_work_tab)).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_no_work_apps_available_share))
+ onView(withText(R.string.resolver_no_work_apps_available))
.check(matches(isDisplayed()));
}
@@ -2146,7 +2146,7 @@
onView(withText(R.string.resolver_work_tab)).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_no_work_apps_available_resolve))
+ onView(withText(R.string.resolver_no_work_apps_available))
.check(matches(isDisplayed()));
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 68287ca..97652a9 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -700,7 +700,7 @@
onView(withText(R.string.resolver_work_tab)).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_no_work_apps_available_resolve))
+ onView(withText(R.string.resolver_no_work_apps_available))
.check(matches(isDisplayed()));
}
@@ -751,7 +751,7 @@
onView(withText(R.string.resolver_work_tab)).perform(click());
waitForIdle();
- onView(withText(R.string.resolver_no_work_apps_available_resolve))
+ onView(withText(R.string.resolver_no_work_apps_available))
.check(matches(isDisplayed()));
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index fc3be13..a18a88c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -96,7 +96,8 @@
.setUsageDurationMillis(
BatteryConsumer.TIME_COMPONENT_CPU, 10300)
.setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400);
+ BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400)
+ .setPowerConsumedByApps(20000);
return builder.build();
}
@@ -148,6 +149,7 @@
assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400);
assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(20300);
+ assertThat(systemBatteryConsumer.getPowerConsumedByApps()).isEqualTo(20000);
} else {
fail("Unexpected drain type " + systemBatteryConsumer.getDrainType());
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index 71d7668..2769b16 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -69,7 +69,7 @@
0.24722, 15000);
assertBluetoothPowerAndDuration(
mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH),
- 0.15833, 9000);
+ 0.51944, 9000, 0.51944, 0.36111);
}
@Test
@@ -98,7 +98,7 @@
0.2, 15000);
assertBluetoothPowerAndDuration(
mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH),
- 0.15, 9000);
+ 0.45, 9000, 0.45, 0.3);
}
private void setDurationsAndPower(
@@ -123,4 +123,15 @@
assertThat(usageDurationMillis).isEqualTo(durationMs);
}
+
+ private void assertBluetoothPowerAndDuration(@Nullable SystemBatteryConsumer batteryConsumer,
+ double powerMah, int durationMs, double consumedPower, double attributedPower) {
+ assertBluetoothPowerAndDuration(batteryConsumer, powerMah, durationMs);
+
+ assertThat(batteryConsumer.getConsumedPower())
+ .isWithin(PRECISION).of(consumedPower);
+
+ assertThat(batteryConsumer.getPowerConsumedByApps())
+ .isWithin(PRECISION).of(attributedPower);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index ae59a54..813bc9f 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -105,7 +105,11 @@
SystemBatteryConsumer consumer =
mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isWithin(PRECISION).of(1.44440);
+ .isWithin(PRECISION).of(2.2444);
+ assertThat(consumer.getConsumedPower())
+ .isWithin(PRECISION).of(2.2444);
+ assertThat(consumer.getPowerConsumedByApps())
+ .isWithin(PRECISION).of(0.8);
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
@@ -156,9 +160,11 @@
SystemBatteryConsumer consumer =
mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
- // 100000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 2.77777 mAh
+ // 100000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) + 1.53934 (apps)= 4.31711 mAh
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isWithin(PRECISION).of(2.77777);
+ .isWithin(PRECISION).of(4.31711);
+ assertThat(consumer.getPowerConsumedByApps())
+ .isWithin(PRECISION).of(1.53934);
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
index f3cf81c..d296afa 100644
--- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
@@ -52,17 +52,26 @@
BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0);
- batteryStats.updateDisplayMeasuredEnergyStatsLocked(0, Display.STATE_ON, 2 * MINUTE_IN_MS);
+ batteryStats.updateDisplayMeasuredEnergyStatsLocked(0, Display.STATE_ON, 0);
+ setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true,
+ 0, 0);
- setFgState(APP_UID1, true, 2 * MINUTE_IN_MS, 2 * MINUTE_IN_MS);
- setFgState(APP_UID1, false, 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
- setFgState(APP_UID2, true, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+ batteryStats.updateDisplayMeasuredEnergyStatsLocked(200_000_000, Display.STATE_ON,
+ 15 * MINUTE_IN_MS);
+
+ setProcState(APP_UID1, ActivityManager.PROCESS_STATE_CACHED_EMPTY, false,
+ 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+
+ setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP, true,
+ 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
batteryStats.updateDisplayMeasuredEnergyStatsLocked(300_000_000, Display.STATE_ON,
60 * MINUTE_IN_MS);
batteryStats.noteScreenStateLocked(Display.STATE_OFF,
80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+ setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP_SLEEPING, false,
+ 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
batteryStats.updateDisplayMeasuredEnergyStatsLocked(100_000_000, Display.STATE_DOZE,
120 * MINUTE_IN_MS);
@@ -79,28 +88,33 @@
assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE))
.isEqualTo(80 * MINUTE_IN_MS);
- // 400000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 111.11111 mAh
+ // 600000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE))
- .isWithin(PRECISION).of(111.11111);
+ .isWithin(PRECISION).of(166.66666);
+ assertThat(consumer.getConsumedPower())
+ .isWithin(PRECISION).of(166.66666);
+ assertThat(consumer.getPowerConsumedByApps())
+ .isWithin(PRECISION).of(166.66666);
UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
assertThat(uid1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN))
- .isEqualTo(18 * MINUTE_IN_MS);
+ .isEqualTo(20 * MINUTE_IN_MS);
- // Uid1 ran for 18 minutes out of the total 48 min of foreground time during the first
- // Display update. Uid1 charge = 18 / 48 * 300000000 uAs = 31.25 mAh
+ // Uid1 took all of the foreground time during the first Display update.
+ // It also ran for 5 out of 45 min during the second Display update:
+ // Uid1 charge = 200000000 + 5 / 45 * 300000000 mAs = 64.81 mAh
assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isWithin(PRECISION).of(31.25);
+ .isWithin(PRECISION).of(64.81481);
UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(uid2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN))
- .isEqualTo(90 * MINUTE_IN_MS);
+ .isEqualTo(60 * MINUTE_IN_MS);
- // Uid2 ran for 30 minutes out of the total 48 min of foreground time during the first
- // Display update and then took all of the time during the second Display update.
- // Uid1 charge = 30 / 48 * 300000000 + 100000000 mAs = 79.86111 mAh
+ // Uid2 ran for 40 minutes out of the total 45 min of foreground time during the second
+ // Display update and then took all of the time during the third Display update.
+ // Uid2 charge = 40 / 45 * 300000000 + 100000000 mAs = 101.85 mAh
assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isWithin(PRECISION).of(79.86111);
+ .isWithin(PRECISION).of(101.85185);
}
@Test
@@ -108,18 +122,22 @@
BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0);
-
- setFgState(APP_UID1, true, 2 * MINUTE_IN_MS, 2 * MINUTE_IN_MS);
+ batteryStats.noteScreenBrightnessLocked(255, 0, 0);
+ setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true,
+ 0, 0);
batteryStats.noteScreenBrightnessLocked(100, 5 * MINUTE_IN_MS, 5 * MINUTE_IN_MS);
batteryStats.noteScreenBrightnessLocked(200, 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
- setFgState(APP_UID1, false, 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
-
- setFgState(APP_UID2, true, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+ setProcState(APP_UID1, ActivityManager.PROCESS_STATE_CACHED_EMPTY, false,
+ 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+ setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP, true,
+ 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
batteryStats.noteScreenStateLocked(Display.STATE_OFF,
80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+ setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP_SLEEPING, false,
+ 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US);
@@ -134,31 +152,39 @@
assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE))
.isEqualTo(80 * MINUTE_IN_MS);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE))
- .isWithin(PRECISION).of(88.4);
+ .isWithin(PRECISION).of(92.0);
+ assertThat(consumer.getConsumedPower())
+ .isWithin(PRECISION).of(92.0);
+ assertThat(consumer.getPowerConsumedByApps())
+ .isWithin(PRECISION).of(92.0);
UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
assertThat(uid1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN))
- .isEqualTo(18 * MINUTE_IN_MS);
+ .isEqualTo(20 * MINUTE_IN_MS);
+
+ // Uid1 took 20 out of the total of 80 min of foreground activity
+ // Uid1 charge = 20 / 80 * 92.0 = 23.0 mAh
assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isWithin(PRECISION).of(14.73333);
+ .isWithin(PRECISION).of(23.0);
UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
assertThat(uid2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN))
- .isEqualTo(90 * MINUTE_IN_MS);
+ .isEqualTo(60 * MINUTE_IN_MS);
+
+ // Uid2 took 60 out of the total of 80 min of foreground activity
+ // Uid2 charge = 60 / 80 * 92.0 = 69.0 mAh
assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isWithin(PRECISION).of(73.66666);
+ .isWithin(PRECISION).of(69.0);
}
- private void setFgState(int uid, boolean fgOn, long realtimeMs, long uptimeMs) {
+ private void setProcState(int uid, int procState, boolean resumed, long realtimeMs,
+ long uptimeMs) {
BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
- if (fgOn) {
+ batteryStats.noteUidProcessStateLocked(uid, procState, realtimeMs, uptimeMs);
+ if (resumed) {
batteryStats.noteActivityResumedLocked(uid, realtimeMs, uptimeMs);
- batteryStats.noteUidProcessStateLocked(uid, ActivityManager.PROCESS_STATE_TOP,
- realtimeMs, uptimeMs);
} else {
batteryStats.noteActivityPausedLocked(uid, realtimeMs, uptimeMs);
- batteryStats.noteUidProcessStateLocked(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
- realtimeMs, uptimeMs);
}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index 2e23dc8..5df91dd 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -99,7 +99,9 @@
assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
.isEqualTo(5577);
assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isWithin(PRECISION).of(0.645200);
+ .isWithin(PRECISION).of(1.11153);
+ assertThat(systemConsumer.getPowerConsumedByApps())
+ .isWithin(PRECISION).of(0.466333);
}
@Test
@@ -125,7 +127,9 @@
.isEqualTo(5577);
/* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */
assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isWithin(PRECISION).of(0.645200 / (0.2214666 + 0.645200) * 1_000_000 / 3600000);
+ .isWithin(PRECISION).of(1.11153 / (0.2214666 + 0.645200) * 1_000_000 / 3600000);
+ assertThat(systemConsumer.getPowerConsumedByApps())
+ .isWithin(PRECISION).of(0.14946);
}
/** Sets up batterystats object with prepopulated network & timer data for Timer-model tests. */
@@ -163,7 +167,9 @@
assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
.isEqualTo(2222);
assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isWithin(PRECISION).of(0.8759216);
+ .isWithin(PRECISION).of(2.575000);
+ assertThat(systemConsumer.getPowerConsumedByApps())
+ .isWithin(PRECISION).of(1.69907);
}
@Test
@@ -190,6 +196,8 @@
.isEqualTo(2222);
/* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */
assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isWithin(PRECISION).of(0.8759216 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000);
+ .isWithin(PRECISION).of(2.575000 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000);
+ assertThat(systemConsumer.getPowerConsumedByApps())
+ .isWithin(PRECISION).of(0.277777);
}
}
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 549e074..5aacfdd 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -13,3 +13,4 @@
yamasani@google.com
per-file preinstalled-packages* = file:/MULTIUSER_OWNERS
+per-file services.core.protolog.json = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/data/etc/car/com.android.car.cluster.home.xml b/data/etc/car/com.android.car.cluster.home.xml
index 4c2d614..832e5a7 100644
--- a/data/etc/car/com.android.car.cluster.home.xml
+++ b/data/etc/car/com.android.car.cluster.home.xml
@@ -16,6 +16,8 @@
-->
<permissions>
<privapp-permissions package="com.android.car.cluster.home">
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.car.permission.ACCESS_PRIVATE_DISPLAY_ID"/>
<permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
</privapp-permissions>
</permissions>
diff --git a/docs/html/reference/images/rounded_corner/rounded-corner-info.png b/docs/html/reference/images/rounded_corner/rounded-corner-info.png
new file mode 100644
index 0000000..312d935
--- /dev/null
+++ b/docs/html/reference/images/rounded_corner/rounded-corner-info.png
Binary files differ
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index d5711c8..f28b66d 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -48,7 +48,6 @@
import android.graphics.Rect;
import android.graphics.Shader;
import android.os.Build;
-import android.os.SystemProperties;
import android.util.AttributeSet;
import android.view.animation.LinearInterpolator;
@@ -152,8 +151,7 @@
private static final int MAX_RIPPLES = 10;
private static final LinearInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
/** Temporary flag for teamfood. **/
- private static final boolean FORCE_PATTERNED_STYLE =
- SystemProperties.getBoolean("persist.material.patternedripple", false);
+ private static final boolean FORCE_PATTERNED_STYLE = true;
private final Rect mTempRect = new Rect();
@@ -319,7 +317,6 @@
hovered = true;
}
}
-
setRippleActive(enabled && pressed);
setBackgroundActive(hovered, focused, pressed);
@@ -819,18 +816,22 @@
if (isBounded()) {
canvas.clipRect(bounds);
}
- float x, y, w, h;
+ final float x, y, cx, cy, w, h;
if (changedHotspotBounds) {
x = mHotspotBounds.exactCenterX();
y = mHotspotBounds.exactCenterY();
- w = mHotspotBounds.width();
+ cx = x;
+ cy = y;
h = mHotspotBounds.height();
+ w = mHotspotBounds.width();
useCanvasProps = false;
} else {
x = mPendingX;
y = mPendingY;
- w = bounds.width();
+ cx = bounds.centerX();
+ cy = bounds.centerY();
h = bounds.height();
+ w = bounds.width();
}
boolean shouldAnimate = mRippleActive;
boolean shouldExit = mExitingAnimation;
@@ -838,10 +839,10 @@
mExitingAnimation = false;
getRipplePaint();
drawContent(canvas);
- drawPatternedBackground(canvas);
+ drawPatternedBackground(canvas, cx, cy);
if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) {
RippleAnimationSession.AnimationProperties<Float, Paint> properties =
- createAnimationProperties(x, y, w, h);
+ createAnimationProperties(x, y, cx, cy, w, h);
mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps)
.setOnAnimationUpdated(() -> invalidateSelf(false))
.setOnSessionEnd(session -> {
@@ -863,19 +864,39 @@
CanvasProperty<Paint>>
p = s.getCanvasProperties();
RecordingCanvas can = (RecordingCanvas) canvas;
- can.drawRipple(p.getX(), p.getY(), p.getMaxRadius(), p.getPaint(),
+ CanvasProperty<Float> xProp, yProp;
+ if (changedHotspotBounds) {
+ xProp = CanvasProperty.createFloat(x);
+ yProp = CanvasProperty.createFloat(y);
+ p.getShader().setTouch(x, y);
+ p.getShader().setOrigin(x, y);
+ } else {
+ xProp = p.getX();
+ yProp = p.getY();
+ }
+ can.drawRipple(xProp, yProp, p.getMaxRadius(), p.getPaint(),
p.getProgress(), p.getShader());
} else {
RippleAnimationSession.AnimationProperties<Float, Paint> p =
s.getProperties();
+ float xProp, yProp;
+ if (changedHotspotBounds) {
+ xProp = x;
+ yProp = y;
+ p.getShader().setTouch(x, y);
+ p.getShader().setOrigin(x, y);
+ } else {
+ xProp = p.getX();
+ yProp = p.getY();
+ }
float radius = p.getMaxRadius();
- canvas.drawCircle(p.getX(), p.getY(), radius, p.getPaint());
+ canvas.drawCircle(xProp, yProp, radius, p.getPaint());
}
}
canvas.restoreToCount(saveCount);
}
- private void drawPatternedBackground(Canvas c) {
+ private void drawPatternedBackground(Canvas c, float cx, float cy) {
if (mRunBackgroundAnimation) {
startBackgroundAnimation();
}
@@ -888,8 +909,7 @@
ColorFilter origFilter = p.getColorFilter();
p.setColorFilter(mMaskColorFilter);
p.setAlpha(alpha);
- Rect b = mHotspotBounds;
- c.drawCircle(b.centerX(), b.centerY(), mState.mMaxRadius, p);
+ c.drawCircle(cx, cy, mState.mMaxRadius, p);
p.setAlpha(origAlpha);
p.setColorFilter(origFilter);
}
@@ -903,7 +923,7 @@
@NonNull
private RippleAnimationSession.AnimationProperties<Float, Paint> createAnimationProperties(
- float x, float y, float w, float h) {
+ float x, float y, float cx, float cy, float w, float h) {
Paint p = new Paint(mRipplePaint);
float radius = mState.mMaxRadius;
RippleAnimationSession.AnimationProperties<Float, Paint> properties;
@@ -912,14 +932,14 @@
? mState.mColor.getColorForState(getState(), Color.BLACK)
: mMaskColorFilter.getColor();
shader.setColor(color);
- shader.setOrigin(w / 2, y / 2);
+ shader.setOrigin(cx, cy);
shader.setTouch(x, y);
shader.setResolution(w, h, mState.mDensity);
shader.setNoisePhase(0);
shader.setRadius(radius);
shader.setProgress(.0f);
properties = new RippleAnimationSession.AnimationProperties<>(
- w / 2, h / 2, radius, p, 0f, shader);
+ cx, cy, radius, p, 0f, shader);
if (mMaskShader == null) {
shader.setShader(null);
} else {
diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java
new file mode 100644
index 0000000..a1a7aa8
--- /dev/null
+++ b/keystore/java/android/security/GenerateRkpKey.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * GenerateKey is a helper class to handle interactions between Keystore and the RemoteProvisioner
+ * app. There are two cases where Keystore should use this class.
+ *
+ * (1) : An app generates a new attested key pair, so Keystore calls notifyKeyGenerated to let the
+ * RemoteProvisioner app check if the state of the attestation key pool is getting low enough
+ * to warrant provisioning more attestation certificates early.
+ *
+ * (2) : An app attempts to generate a new key pair, but the keystore service discovers it is out of
+ * attestation key pairs and cannot provide one for the given application. Keystore can then
+ * make a blocking call on notifyEmpty to allow the RemoteProvisioner app to get another
+ * attestation certificate chain provisioned.
+ *
+ * In most cases, the proper usage of (1) should preclude the need for (2).
+ *
+ * @hide
+ */
+public class GenerateRkpKey {
+
+ private IGenerateRkpKeyService mBinder;
+ private Context mContext;
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mBinder = IGenerateRkpKeyService.Stub.asInterface(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ mBinder = null;
+ }
+ };
+
+ /**
+ * Constructor which takes a Context object.
+ */
+ public GenerateRkpKey(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Fulfills the use case of (2) described in the class documentation. Blocks until the
+ * RemoteProvisioner application can get new attestation keys signed by the server.
+ */
+ public void notifyEmpty(int securityLevel) throws RemoteException {
+ Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
+ ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ intent.setComponent(comp);
+ if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+ throw new RemoteException("Failed to bind to GenerateKeyService");
+ }
+ if (mBinder != null) {
+ mBinder.generateKey(securityLevel);
+ }
+ mContext.unbindService(mConnection);
+ }
+
+ /**
+ * FUlfills the use case of (1) described in the class documentation. Non blocking call.
+ */
+ public void notifyKeyGenerated(int securityLevel) throws RemoteException {
+ Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
+ ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ intent.setComponent(comp);
+ if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+ throw new RemoteException("Failed to bind to GenerateKeyService");
+ }
+ if (mBinder != null) {
+ mBinder.notifyKeyGenerated(securityLevel);
+ }
+ mContext.unbindService(mConnection);
+ }
+}
diff --git a/keystore/java/android/security/GenerateRkpKeyException.java b/keystore/java/android/security/GenerateRkpKeyException.java
new file mode 100644
index 0000000..a2d65e4
--- /dev/null
+++ b/keystore/java/android/security/GenerateRkpKeyException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * Thrown on problems in attempting to attest to a key using a remotely provisioned key.
+ *
+ * @hide
+ */
+public class GenerateRkpKeyException extends Exception {
+
+ /**
+ * Constructs a new {@code GenerateRkpKeyException}.
+ */
+ public GenerateRkpKeyException() {
+ }
+}
diff --git a/keystore/java/android/security/IGenerateRkpKeyService.aidl b/keystore/java/android/security/IGenerateRkpKeyService.aidl
new file mode 100644
index 0000000..5f1d669
--- /dev/null
+++ b/keystore/java/android/security/IGenerateRkpKeyService.aidl
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * Interface to allow the framework to notify the RemoteProvisioner app when keys are empty. This
+ * will be used if Keystore replies with an error code NO_KEYS_AVAILABLE in response to an
+ * attestation request. The framework can then synchronously call generateKey() to get more
+ * attestation keys generated and signed. Upon return, the caller can be certain an attestation key
+ * is available.
+ *
+ * @hide
+ */
+interface IGenerateRkpKeyService {
+ /**
+ * Ping the provisioner service to let it know an app generated a key. This may or may not have
+ * consumed a remotely provisioned attestation key, so the RemoteProvisioner app should check.
+ */
+ oneway void notifyKeyGenerated(in int securityLevel);
+ /** Ping the provisioner service to indicate there are no remaining attestation keys left. */
+ void generateKey(in int securityLevel);
+}
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 75e248e..df579bb 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -125,6 +125,8 @@
}
}
+ private static final String KEYSTORE2_SERVICE_NAME =
+ "android.system.keystore2.IKeystoreService/default";
private KeyStore2() {
mBinder = null;
@@ -137,7 +139,7 @@
private synchronized IKeystoreService getService(boolean retryLookup) {
if (mBinder == null || retryLookup) {
mBinder = IKeystoreService.Stub.asInterface(ServiceManager
- .getService("android.system.keystore2"));
+ .getService(KEYSTORE2_SERVICE_NAME));
}
return mBinder;
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index e401add..2d8901a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -24,6 +24,9 @@
import android.hardware.security.keymint.SecurityLevel;
import android.hardware.security.keymint.Tag;
import android.os.Build;
+import android.os.RemoteException;
+import android.security.GenerateRkpKey;
+import android.security.GenerateRkpKeyException;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore;
import android.security.KeyStore2;
@@ -520,6 +523,18 @@
@Override
public KeyPair generateKeyPair() {
+ try {
+ return generateKeyPairHelper();
+ } catch (GenerateRkpKeyException e) {
+ try {
+ return generateKeyPairHelper();
+ } catch (GenerateRkpKeyException f) {
+ throw new ProviderException("Failed to provision new attestation keys.");
+ }
+ }
+ }
+
+ private KeyPair generateKeyPairHelper() throws GenerateRkpKeyException {
if (mKeyStore == null || mSpec == null) {
throw new IllegalStateException("Not initialized");
}
@@ -557,13 +572,30 @@
AndroidKeyStorePublicKey publicKey =
AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse(
descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm);
-
+ GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext());
+ try {
+ if (mSpec.getAttestationChallenge() != null) {
+ keyGen.notifyKeyGenerated(securityLevel);
+ }
+ } catch (RemoteException e) {
+ // This is not really an error state, and necessarily does not apply to non RKP
+ // systems or hybrid systems where RKP is not currently turned on.
+ Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend.");
+ }
success = true;
return new KeyPair(publicKey, publicKey.getPrivateKey());
} catch (android.security.KeyStoreException e) {
switch(e.getErrorCode()) {
case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
throw new StrongBoxUnavailableException("Failed to generated key pair.", e);
+ case ResponseCode.OUT_OF_KEYS:
+ GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext());
+ try {
+ keyGen.notifyEmpty(securityLevel);
+ } catch (RemoteException f) {
+ throw new ProviderException("Failed to talk to RemoteProvisioner", f);
+ }
+ throw new GenerateRkpKeyException();
default:
ProviderException p = new ProviderException("Failed to generate key pair.", e);
if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index b75ee45..cd63de8 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -22,9 +22,9 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"הגדרות"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"תפריט"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> במצב תמונה בתוך תמונה"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולכבות את התכונה."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולהשבית את התכונה."</string>
<string name="pip_play" msgid="3496151081459417097">"הפעלה"</string>
- <string name="pip_pause" msgid="690688849510295232">"השהה"</string>
+ <string name="pip_pause" msgid="690688849510295232">"השהיה"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"אפשר לדלג אל הבא"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"אפשר לדלג אל הקודם"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"שינוי גודל"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 1527e89..fcd706f 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -58,14 +58,14 @@
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels weergeven"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels tonen"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatten met bubbels"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden weergegeven als zwevende iconen of \'bubbels\'. Tik om een bubbel te openen. Sleep om de bubbel te verplaatsen."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden als zwevende iconen of bubbels getoond. Tik om een bubbel te openen. Sleep om een bubbel te verplaatsen."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Beheer bubbels wanneer je wilt"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te schakelen"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels worden hier weergegeven"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels zie je hier"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index bacff78..b64c796 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -27,9 +27,10 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Rect;
import android.view.SurfaceControl;
+import android.view.WindowInsets;
+import android.view.WindowManager;
import androidx.annotation.Nullable;
@@ -75,7 +76,7 @@
mDividerSize = mDividerWindowWidth - mDividerInsets * 2;
mRootBounds.set(configuration.windowConfiguration.getBounds());
- mDividerSnapAlgorithm = getSnapAlgorithm(context.getResources(), mRootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
resetDividerPosition();
}
@@ -114,7 +115,7 @@
mContext = mContext.createConfigurationContext(configuration);
mSplitWindowManager.setConfiguration(configuration);
mRootBounds.set(rootBounds);
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext.getResources(), mRootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
resetDividerPosition();
// Don't inflate divider bar if it is not initialized.
@@ -217,15 +218,15 @@
return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss);
}
- private DividerSnapAlgorithm getSnapAlgorithm(Resources resources, Rect rootBounds) {
+ private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds) {
final boolean isLandscape = isLandscape(rootBounds);
return new DividerSnapAlgorithm(
- resources,
+ context.getResources(),
rootBounds.width(),
rootBounds.height(),
mDividerSize,
!isLandscape,
- new Rect() /* insets */,
+ getDisplayInsets(context),
isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
}
@@ -250,6 +251,15 @@
animator.start();
}
+ private static Rect getDisplayInsets(Context context) {
+ return context.getSystemService(WindowManager.class)
+ .getMaximumWindowMetrics()
+ .getWindowInsets()
+ .getInsets(WindowInsets.Type.navigationBars()
+ | WindowInsets.Type.statusBars()
+ | WindowInsets.Type.displayCutout()).toRect();
+ }
+
private static boolean isLandscape(Rect bounds) {
return bounds.width() > bounds.height();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
index f4c0f93..cf35656 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
@@ -67,7 +67,7 @@
final SurfaceSession mSurfaceSession = new SurfaceSession();
- private final SplitScreenTransitions mSplitTransitions;
+ private final LegacySplitScreenTransitions mSplitTransitions;
LegacySplitScreenTaskListener(LegacySplitScreenController splitScreenController,
ShellTaskOrganizer shellTaskOrganizer,
@@ -75,7 +75,7 @@
SyncTransactionQueue syncQueue) {
mSplitScreenController = splitScreenController;
mTaskOrganizer = shellTaskOrganizer;
- mSplitTransitions = new SplitScreenTransitions(splitScreenController.mTransactionPool,
+ mSplitTransitions = new LegacySplitScreenTransitions(splitScreenController.mTransactionPool,
transitions, mSplitScreenController, this);
transitions.addHandler(mSplitTransitions);
mSyncQueue = syncQueue;
@@ -112,7 +112,7 @@
return mTaskOrganizer;
}
- SplitScreenTransitions getSplitTransitions() {
+ LegacySplitScreenTransitions getSplitTransitions() {
return mSplitTransitions;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
index d06064a..27c56fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
@@ -47,7 +47,7 @@
import java.util.ArrayList;
/** Plays transition animations for split-screen */
-public class SplitScreenTransitions implements Transitions.TransitionHandler {
+public class LegacySplitScreenTransitions implements Transitions.TransitionHandler {
private static final String TAG = "SplitScreenTransitions";
public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 10;
@@ -68,7 +68,7 @@
private Transitions.TransitionFinishCallback mFinishCallback = null;
private SurfaceControl.Transaction mFinishTransaction;
- SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
+ LegacySplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
@NonNull LegacySplitScreenController splitScreen,
@NonNull LegacySplitScreenTaskListener listener) {
mTransactionPool = pool;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
index d90cc47..c7dbe88 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
@@ -23,7 +23,6 @@
import android.util.Log;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
-import android.view.WindowManager;
import android.window.DisplayAreaAppearedInfo;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
@@ -34,8 +33,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
+import java.io.PrintWriter;
import java.util.List;
import java.util.concurrent.Executor;
@@ -52,12 +52,15 @@
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final float[] mColor;
private final float mAlpha;
- private final Rect mRect;
private final Executor mMainExecutor;
- private final Rect mDisplaySize;
private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
+ /**
+ * The background to distinguish the boundary of translated windows and empty region when
+ * one handed mode triggered.
+ */
+ private Rect mBkgBounds;
@VisibleForTesting
@GuardedBy("mLock")
boolean mIsShowing;
@@ -82,15 +85,19 @@
mMainExecutor.execute(() -> removeBackgroundPanelLayer());
}
- public OneHandedBackgroundPanelOrganizer(Context context, WindowManager windowManager,
- DisplayController displayController, Executor executor) {
+ public OneHandedBackgroundPanelOrganizer(Context context, DisplayLayout displayLayout,
+ Executor executor) {
super(executor);
- mDisplaySize = windowManager.getCurrentWindowMetrics().getBounds();
final Resources res = context.getResources();
final float defaultRGB = res.getFloat(R.dimen.config_one_handed_background_rgb);
mColor = new float[]{defaultRGB, defaultRGB, defaultRGB};
mAlpha = res.getFloat(R.dimen.config_one_handed_background_alpha);
- mRect = new Rect(0, 0, mDisplaySize.width(), mDisplaySize.height());
+ // Ensure the mBkgBounds is portrait, due to OHM only support on portrait
+ if (displayLayout.height() > displayLayout.width()) {
+ mBkgBounds = new Rect(0, 0, displayLayout.width(), displayLayout.height());
+ } else {
+ mBkgBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width());
+ }
mMainExecutor = executor;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
}
@@ -144,6 +151,7 @@
if (mBackgroundSurface == null) {
mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession)
.setParent(mParentLeash)
+ .setBufferSize(mBkgBounds.width(), mBkgBounds.height())
.setColorLayer()
.setFormat(PixelFormat.RGBA_8888)
.setOpaque(false)
@@ -188,11 +196,19 @@
SurfaceControl.Transaction transaction =
mSurfaceControlTransactionFactory.getTransaction();
- transaction.remove(mBackgroundSurface);
- transaction.apply();
+ transaction.remove(mBackgroundSurface).apply();
transaction.close();
mBackgroundSurface = null;
mIsShowing = false;
}
}
+
+ void dump(@NonNull PrintWriter pw) {
+ final String innerPrefix = " ";
+ pw.println(TAG + "states: ");
+ pw.print(innerPrefix + "mIsShowing=");
+ pw.println(mIsShowing);
+ pw.print(innerPrefix + "mBkgBounds=");
+ pw.println(mBkgBounds);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 25968eb..7965a80 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -17,10 +17,10 @@
package com.android.wm.shell.onehanded;
import static android.os.UserHandle.USER_CURRENT;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-import android.Manifest;
import android.annotation.BinderThread;
import android.content.ComponentName;
import android.content.Context;
@@ -28,13 +28,13 @@
import android.content.om.OverlayInfo;
import android.content.res.Configuration;
import android.database.ContentObserver;
-import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Slog;
+import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -47,7 +47,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.ExecutorUtils;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
@@ -76,9 +76,10 @@
private boolean mLockedDisabled;
private float mOffSetFraction;
- private final Context mContext;
+ private Context mContext;
+
+ private final AccessibilityManager mAccessibilityManager;
private final DisplayController mDisplayController;
- private final OneHandedGestureHandler mGestureHandler;
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
private final OneHandedTimeoutHandler mTimeoutHandler;
private final OneHandedTouchHandler mTouchHandler;
@@ -89,10 +90,9 @@
private final ShellExecutor mMainExecutor;
private final Handler mMainHandler;
private final OneHandedImpl mImpl = new OneHandedImpl();
- private final WindowManager mWindowManager;
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
- private final AccessibilityManager mAccessibilityManager;
+ private OneHandedGestureHandler mGestureHandler;
private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
/**
@@ -100,8 +100,29 @@
*/
private final DisplayChangeController.OnDisplayChangingListener mRotationController =
(display, fromRotation, toRotation, wct) -> {
- if (mDisplayAreaOrganizer != null) {
- mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation, wct);
+ if (!isInitialized()) {
+ return;
+ }
+ mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct);
+ mGestureHandler.onRotateDisplay(mDisplayAreaOrganizer.getDisplayLayout());
+ };
+
+ private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
+ new DisplayController.OnDisplaysChangedListener() {
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != DEFAULT_DISPLAY || !isInitialized()) {
+ return;
+ }
+ updateDisplayLayout(displayId);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (displayId != DEFAULT_DISPLAY || !isInitialized()) {
+ return;
+ }
+ updateDisplayLayout(displayId);
}
};
@@ -115,8 +136,7 @@
new AccessibilityManager.AccessibilityStateChangeListener() {
@Override
public void onAccessibilityStateChanged(boolean enabled) {
- if (mOneHandedSettingsUtil == null) {
- Slog.w(TAG, "mOneHandedSettingsUtil may not instantiate yet");
+ if (!isInitialized()) {
return;
}
if (enabled) {
@@ -147,6 +167,14 @@
}
};
+ private boolean isInitialized() {
+ if (mDisplayAreaOrganizer == null || mDisplayController == null
+ || mGestureHandler == null || mOneHandedSettingsUtil == null) {
+ Slog.w(TAG, "Components may not initialized yet!");
+ return false;
+ }
+ return true;
+ }
/**
* Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
@@ -154,8 +182,8 @@
@Nullable
public static OneHandedController create(
Context context, WindowManager windowManager, DisplayController displayController,
- TaskStackListenerImpl taskStackListener, UiEventLogger uiEventLogger,
- ShellExecutor mainExecutor, Handler mainHandler) {
+ DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
+ UiEventLogger uiEventLogger, ShellExecutor mainExecutor, Handler mainHandler) {
if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
Slog.w(TAG, "Device doesn't support OneHanded feature");
return null;
@@ -169,19 +197,17 @@
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
mainExecutor);
OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler(
- context, windowManager, displayController, ViewConfiguration.get(context),
- mainExecutor);
+ context, displayLayout, ViewConfiguration.get(context), mainExecutor);
OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
- new OneHandedBackgroundPanelOrganizer(context, windowManager, displayController,
- mainExecutor);
+ new OneHandedBackgroundPanelOrganizer(context, displayLayout, mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
- context, windowManager, animationController, tutorialHandler,
+ context, displayLayout, animationController, tutorialHandler,
oneHandedBackgroundPanelOrganizer, mainExecutor);
OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
- return new OneHandedController(context, windowManager, displayController,
+ return new OneHandedController(context, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
gestureHandler, settingsUtil, timeoutHandler, oneHandedUiEventsLogger,
overlayManager, taskStackListener, mainExecutor, mainHandler);
@@ -189,7 +215,6 @@
@VisibleForTesting
OneHandedController(Context context,
- WindowManager windowManager,
DisplayController displayController,
OneHandedBackgroundPanelOrganizer backgroundPanelOrganizer,
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
@@ -205,7 +230,6 @@
Handler mainHandler) {
mContext = context;
mOneHandedSettingsUtil = settingsUtil;
- mWindowManager = windowManager;
mBackgroundPanelOrganizer = backgroundPanelOrganizer;
mDisplayAreaOrganizer = displayAreaOrganizer;
mDisplayController = displayController;
@@ -218,6 +242,7 @@
mOneHandedUiEventLogger = uiEventsLogger;
mTaskStackListener = taskStackListener;
+ mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
final int sysPropPercentageConfig = SystemProperties.getInt(
@@ -297,8 +322,14 @@
Slog.d(TAG, "Temporary lock disabled");
return;
}
+ final int currentRotation = mDisplayAreaOrganizer.getDisplayLayout().rotation();
+ if (currentRotation != Surface.ROTATION_0 && currentRotation != Surface.ROTATION_180) {
+ Slog.w(TAG, "One handed mode only support portrait mode");
+ return;
+ }
if (!mDisplayAreaOrganizer.isInOneHanded()) {
- final int yOffSet = Math.round(getDisplaySize().height() * mOffSetFraction);
+ final int yOffSet = Math.round(
+ mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
mTimeoutHandler.resetTimer();
@@ -371,6 +402,12 @@
.getSettingsSwipeToNotificationEnabled(mContext.getContentResolver()));
}
+ private void updateDisplayLayout(int displayId) {
+ mDisplayAreaOrganizer.setDisplayLayout(
+ mDisplayController.getDisplayLayout(displayId));
+ mGestureHandler.onDisplayChanged(mDisplayAreaOrganizer.getDisplayLayout());
+ }
+
private ContentObserver getObserver(Runnable onChangeRunnable) {
return new ContentObserver(mMainHandler) {
@Override
@@ -454,24 +491,6 @@
OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT));
}
- /**
- * Query the current display real size from {@link WindowManager}
- *
- * @return {@link WindowManager#getCurrentWindowMetrics()#getBounds()}
- */
- private Rect getDisplaySize() {
- if (mWindowManager == null) {
- Slog.e(TAG, "WindowManager instance is null! Can not get display size!");
- return new Rect();
- }
- final Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds();
- if (displaySize.width() == 0 || displaySize.height() == 0) {
- Slog.e(TAG, "Display size error! width = " + displaySize.width()
- + ", height = " + displaySize.height());
- }
- return displaySize;
- }
-
@VisibleForTesting
boolean isLockedDisabled() {
return mLockedDisabled;
@@ -483,7 +502,7 @@
}
mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
- mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled);
+ mGestureHandler.onGestureEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled);
if (!mIsOneHandedEnabled) {
mDisplayAreaOrganizer.unregisterOrganizer();
@@ -532,10 +551,15 @@
@VisibleForTesting
void setLockedDisabled(boolean locked, boolean enabled) {
- if (enabled == mIsOneHandedEnabled) {
+ final boolean isFeatureEnabled = mIsOneHandedEnabled || mIsSwipeToNotificationEnabled;
+
+ if (enabled == isFeatureEnabled) {
return;
}
mLockedDisabled = locked && !enabled;
+
+ // Disabled gesture when keyguard ON
+ mGestureHandler.onGestureEnabled(!mLockedDisabled && isFeatureEnabled);
}
private void onConfigChanged(Configuration newConfig) {
@@ -556,6 +580,10 @@
pw.print(innerPrefix + "mLockedDisabled=");
pw.println(mLockedDisabled);
+ if (mBackgroundPanelOrganizer != null) {
+ mBackgroundPanelOrganizer.dump(pw);
+ }
+
if (mDisplayAreaOrganizer != null) {
mDisplayAreaOrganizer.dump(pw);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 5eec231..682c9a3f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -23,9 +23,7 @@
import android.graphics.Rect;
import android.os.SystemProperties;
import android.util.ArrayMap;
-import android.util.Slog;
import android.view.SurfaceControl;
-import android.view.WindowManager;
import android.window.DisplayAreaAppearedInfo;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
@@ -37,6 +35,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
@@ -58,7 +57,8 @@
private static final String ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION =
"persist.debug.one_handed_translate_animation_duration";
- private final WindowManager mWindowManager;
+ private DisplayLayout mDisplayLayout = new DisplayLayout();
+
private final Rect mLastVisualDisplayBounds = new Rect();
private final Rect mDefaultDisplayBounds = new Rect();
@@ -108,15 +108,15 @@
* Constructor of OneHandedDisplayAreaOrganizer
*/
public OneHandedDisplayAreaOrganizer(Context context,
- WindowManager windowManager,
+ DisplayLayout displayLayout,
OneHandedAnimationController animationController,
OneHandedTutorialHandler tutorialHandler,
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
ShellExecutor mainExecutor) {
super(mainExecutor);
- mWindowManager = windowManager;
+ mDisplayLayout.set(displayLayout);
+ updateDisplayBounds();
mAnimationController = animationController;
- mLastVisualDisplayBounds.set(getDisplayBounds());
final int animationDurationConfig = context.getResources().getInteger(
R.integer.config_one_handed_translate_animation_duration);
mEnterExitAnimationDurationMs =
@@ -146,7 +146,7 @@
final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
}
- mDefaultDisplayBounds.set(getDisplayBounds());
+ updateDisplayBounds();
return displayAreaInfos;
}
@@ -157,29 +157,21 @@
}
/**
- * Handler for display rotation changes by below policy which
- * handles 90 degree display rotation changes {@link Surface.Rotation}.
+ * Handler for display rotation changes by {@link DisplayLayout}
*
- * @param fromRotation starting rotation of the display.
- * @param toRotation target rotation of the display (after rotating).
- * @param wct A task transaction {@link WindowContainerTransaction} from
- * {@link DisplayChangeController} to populate.
+ * @param context Any context
+ * @param toRotation target rotation of the display (after rotating).
+ * @param wct A task transaction {@link WindowContainerTransaction} from
+ * {@link DisplayChangeController} to populate.
*/
- public void onRotateDisplay(int fromRotation, int toRotation, WindowContainerTransaction wct) {
- // Stop one handed without animation and reset cropped size immediately
- final Rect newBounds = new Rect(getDisplayBounds());
- // This diff rule will only filter the cases portrait <-> landscape
- final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1;
-
- if (isOrientationDiff) {
- // getDisplayBounds() will return window metrics bounds which dose not update to
- // corresponding display orientation yet, we have to manual rotate bounds
- newBounds.set(0, 0, newBounds.bottom, newBounds.right);
- resetWindowsOffset(wct);
- mDefaultDisplayBounds.set(newBounds);
- mLastVisualDisplayBounds.set(newBounds);
- finishOffset(0, TRANSITION_DIRECTION_EXIT);
+ public void onRotateDisplay(Context context, int toRotation, WindowContainerTransaction wct) {
+ if (mDisplayLayout.rotation() == toRotation) {
+ return;
}
+ mDisplayLayout.rotateTo(context.getResources(), toRotation);
+ resetWindowsOffset(wct);
+ updateDisplayBounds();
+ finishOffset(0, TRANSITION_DIRECTION_EXIT);
}
/**
@@ -191,9 +183,7 @@
mDefaultDisplayBounds.top + yOffset,
mDefaultDisplayBounds.right,
mDefaultDisplayBounds.bottom + yOffset);
- final Rect fromBounds = getLastVisualDisplayBounds() != null
- ? getLastVisualDisplayBounds()
- : mDefaultDisplayBounds;
+ final Rect fromBounds = getLastVisualDisplayBounds();
final int direction = yOffset > 0
? TRANSITION_DIRECTION_TRIGGER
: TRANSITION_DIRECTION_EXIT;
@@ -219,7 +209,8 @@
applyTransaction(wct);
}
- private void resetWindowsOffset(WindowContainerTransaction wct) {
+ @VisibleForTesting
+ void resetWindowsOffset(WindowContainerTransaction wct) {
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
mDisplayAreaTokenMap.forEach(
@@ -292,19 +283,19 @@
return mLastVisualDisplayBounds;
}
- @Nullable
@VisibleForTesting
- Rect getDisplayBounds() {
- if (mWindowManager == null) {
- Slog.e(TAG, "WindowManager instance is null! Can not get display size!");
- return new Rect();
- }
- final Rect displayBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
- if (displayBounds.width() == 0 || displayBounds.height() == 0) {
- Slog.e(TAG, "Display size error! width = " + displayBounds.width()
- + ", height = " + displayBounds.height());
- }
- return displayBounds;
+ @Nullable
+ Rect getLastDisplayBounds() {
+ return mLastVisualDisplayBounds;
+ }
+
+ public DisplayLayout getDisplayLayout() {
+ return mDisplayLayout;
+ }
+
+ @VisibleForTesting
+ void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
+ mDisplayLayout.set(displayLayout);
}
@VisibleForTesting
@@ -312,6 +303,11 @@
return mDisplayAreaTokenMap;
}
+ void updateDisplayBounds() {
+ mDefaultDisplayBounds.set(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+ mLastVisualDisplayBounds.set(mDefaultDisplayBounds);
+ }
+
/**
* Register transition callback
*/
@@ -324,13 +320,13 @@
pw.println(TAG + "states: ");
pw.print(innerPrefix + "mIsInOneHanded=");
pw.println(mIsInOneHanded);
+ pw.print(innerPrefix + "mDisplayLayout.rotation()=");
+ pw.println(mDisplayLayout.rotation());
pw.print(innerPrefix + "mDisplayAreaTokenMap=");
pw.println(mDisplayAreaTokenMap);
pw.print(innerPrefix + "mDefaultDisplayBounds=");
pw.println(mDefaultDisplayBounds);
pw.print(innerPrefix + "mLastVisualDisplayBounds=");
pw.println(mLastVisualDisplayBounds);
- pw.print(innerPrefix + "getDisplayBounds()=");
- pw.println(getDisplayBounds());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
index 778876c..495362a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
@@ -24,7 +24,6 @@
import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.os.Looper;
-import android.util.Log;
import android.view.Display;
import android.view.InputChannel;
import android.view.InputEvent;
@@ -33,29 +32,23 @@
import android.view.MotionEvent;
import android.view.Surface;
import android.view.ViewConfiguration;
-import android.view.WindowManager;
-import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayChangeController;
-import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
/**
- * The class manage swipe up and down gesture for 3-Button mode navigation,
- * others(e.g, 2-button, full gesture mode) are handled by Launcher quick steps.
- * TODO(b/160934654) Migrate to Launcher quick steps
+ * The class manage swipe up and down gesture for 3-Button mode navigation, others(e.g, 2-button,
+ * full gesture mode) are handled by Launcher quick steps. TODO(b/160934654) Migrate to Launcher
+ * quick steps
*/
-public class OneHandedGestureHandler implements OneHandedTransitionCallback,
- DisplayChangeController.OnDisplayChangingListener {
+public class OneHandedGestureHandler implements OneHandedTransitionCallback {
private static final String TAG = "OneHandedGestureHandler";
- private static final boolean DEBUG_GESTURE = false;
private static final int ANGLE_MAX = 150;
private static final int ANGLE_MIN = 30;
@@ -64,14 +57,13 @@
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
private final PointF mStartDragPos = new PointF();
- private final WindowManager mWindowManager;
private boolean mPassedSlop;
private boolean mAllowGesture;
private boolean mIsEnabled;
private int mNavGestureHeight;
private boolean mIsThreeButtonModeEnabled;
- private int mRotation = Surface.ROTATION_0;
+ private int mRotation;
@VisibleForTesting
InputMonitor mInputMonitor;
@@ -85,37 +77,35 @@
private boolean mIsStopGesture;
/**
- * Constructor of OneHandedGestureHandler, we only handle the gesture of
- * {@link Display#DEFAULT_DISPLAY}
+ * Constructor of OneHandedGestureHandler, we only handle the gesture of {@link
+ * Display#DEFAULT_DISPLAY}
*
- * @param context {@link Context}
- * @param displayController {@link DisplayController}
+ * @param context Any context
+ * @param displayLayout Current {@link DisplayLayout} from controller
+ * @param viewConfig {@link ViewConfiguration} to obtain touch slop
+ * @param mainExecutor The wm-shell main executor
*/
- public OneHandedGestureHandler(Context context, WindowManager windowManager,
- DisplayController displayController, ViewConfiguration viewConfig,
+ public OneHandedGestureHandler(Context context,
+ DisplayLayout displayLayout,
+ ViewConfiguration viewConfig,
ShellExecutor mainExecutor) {
- mWindowManager = windowManager;
mMainExecutor = mainExecutor;
- displayController.addDisplayChangingController(this);
- mNavGestureHeight = getNavBarSize(context,
- displayController.getDisplayLayout(DEFAULT_DISPLAY));
mDragDistThreshold = context.getResources().getDimensionPixelSize(
R.dimen.gestures_onehanded_drag_threshold);
+
final float slop = viewConfig.getScaledTouchSlop();
mSquaredSlop = slop * slop;
-
+ onDisplayChanged(displayLayout);
updateIsEnabled();
}
/**
- * Notified by {@link OneHandedController}, when user update settings of Enabled or Disabled
+ * Notifies by {@link OneHandedController}, when swipe down gesture is enabled on 3 button
+ * navigation bar mode.
*
- * @param isEnabled is one handed settings enabled or not
+ * @param isEnabled Either one handed mode or swipe for notification function enabled or not
*/
- public void onOneHandedEnabled(boolean isEnabled) {
- if (DEBUG_GESTURE) {
- Log.d(TAG, "onOneHandedEnabled, isEnabled = " + isEnabled);
- }
+ public void onGestureEnabled(boolean isEnabled) {
mIsEnabled = isEnabled;
updateIsEnabled();
}
@@ -126,25 +116,31 @@
}
/**
- * Register {@link OneHandedGestureEventCallback} to receive onStart(), onStop() callback
+ * Registers {@link OneHandedGestureEventCallback} to receive onStart(), onStop() callback
*/
public void setGestureEventListener(OneHandedGestureEventCallback callback) {
mGestureEventCallback = callback;
}
+ /**
+ * Called when onDisplayAdded() or onDisplayRemoved() callback
+ * @param displayLayout The latest {@link DisplayLayout} representing current displayId
+ */
+ public void onDisplayChanged(DisplayLayout displayLayout) {
+ mNavGestureHeight = getNavBarSize(displayLayout);
+ mGestureRegion.set(0, displayLayout.height() - mNavGestureHeight, displayLayout.width(),
+ displayLayout.height());
+ mRotation = displayLayout.rotation();
+ }
+
private void onMotionEvent(MotionEvent ev) {
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
- mAllowGesture = isWithinTouchRegion(ev.getX(), ev.getY())
- && mRotation == Surface.ROTATION_0;
+ mAllowGesture = isWithinTouchRegion(ev.getX(), ev.getY()) && isGestureAvailable();
if (mAllowGesture) {
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
}
- if (DEBUG_GESTURE) {
- Log.d(TAG, "ACTION_DOWN, mDownPos=" + mDownPos + ", mAllowGesture="
- + mAllowGesture);
- }
} else if (mAllowGesture) {
switch (action) {
case MotionEvent.ACTION_MOVE:
@@ -204,34 +200,17 @@
}
private boolean isWithinTouchRegion(float x, float y) {
- if (DEBUG_GESTURE) {
- Log.d(TAG, "isWithinTouchRegion(), mGestureRegion=" + mGestureRegion + ", downX=" + x
- + ", downY=" + y);
- }
return mGestureRegion.contains(Math.round(x), Math.round(y));
}
- private int getNavBarSize(Context context, @Nullable DisplayLayout displayLayout) {
- if (displayLayout != null) {
- return displayLayout.navBarFrameHeight();
- } else {
- return isRotated()
- ? context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height_landscape)
- : context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height);
- }
+ private int getNavBarSize(@NonNull DisplayLayout displayLayout) {
+ return isGestureAvailable() ? displayLayout.navBarFrameHeight() : 0 /* In landscape */;
}
private void updateIsEnabled() {
disposeInputChannel();
- // Either OHM or swipe notification shade can activate in portrait mode only
- if (mIsEnabled && mIsThreeButtonModeEnabled && !isRotated()) {
- final Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds();
- // Register input event receiver to monitor the touch region of NavBar gesture height
- mGestureRegion.set(0, displaySize.height() - mNavGestureHeight, displaySize.width(),
- displaySize.height());
+ if (mIsEnabled && mIsThreeButtonModeEnabled && isGestureAvailable()) {
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"onehanded-gesture-offset", DEFAULT_DISPLAY);
try {
@@ -251,10 +230,16 @@
}
}
- @Override
- public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
- WindowContainerTransaction t) {
- mRotation = toRotation;
+ /**
+ * Handler for display rotation changes by {@link DisplayLayout}
+ *
+ * @param displayLayout The rotated displayLayout
+ */
+ public void onRotateDisplay(DisplayLayout displayLayout) {
+ mRotation = displayLayout.rotation();
+ mNavGestureHeight = getNavBarSize(displayLayout);
+ mGestureRegion.set(0, displayLayout.height() - mNavGestureHeight, displayLayout.width(),
+ displayLayout.height());
updateIsEnabled();
}
@@ -270,8 +255,9 @@
}
}
- private boolean isRotated() {
- return mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
+ private boolean isGestureAvailable() {
+ // Either OHM or swipe notification shade can activate in portrait mode only
+ return mRotation == Surface.ROTATION_0 || mRotation == Surface.ROTATION_180;
}
private boolean isValidStartAngle(float deltaX, float deltaY) {
@@ -291,14 +277,18 @@
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG + "States: ");
+ pw.print(innerPrefix + "mAllowGesture=");
+ pw.println(mAllowGesture);
pw.print(innerPrefix + "mIsEnabled=");
pw.println(mIsEnabled);
+ pw.print(innerPrefix + "mGestureRegion=");
+ pw.println(mGestureRegion);
pw.print(innerPrefix + "mNavGestureHeight=");
pw.println(mNavGestureHeight);
pw.print(innerPrefix + "mIsThreeButtonModeEnabled=");
pw.println(mIsThreeButtonModeEnabled);
- pw.print(innerPrefix + "isLandscape=");
- pw.println(isRotated());
+ pw.print(innerPrefix + "mRotation=");
+ pw.println(mRotation);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index e7cd38f..01a81d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -40,15 +40,17 @@
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
WindowContainerTransaction wct) {
final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.setHidden(rootToken, false)
- .setBounds(rootToken, rootBounds)
+ wct.setBounds(rootToken, rootBounds)
.reparent(task.token, rootToken, true /* onTop*/)
// Moving the root task to top after the child tasks were repareted , or the root
// task cannot be visible and focused.
- .reorder(rootToken, true);
+ .reorder(rootToken, true /* onTop */);
}
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
+ // No matter if the root task is empty or not, moving the root to bottom because it no
+ // longer preserves visible child task.
+ wct.reorder(mRootTaskInfo.token, false /* onTop */);
if (mChildrenTaskInfo.size() == 0) return false;
wct.reparentTasks(
mRootTaskInfo.token,
@@ -62,10 +64,7 @@
boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
if (task == null) return false;
-
- wct.setHidden(mRootTaskInfo.token, true)
- .reorder(mRootTaskInfo.token, false)
- .reparent(task.token, newParent, false /* onTop */);
+ wct.reparent(task.token, newParent, false /* onTop */);
return true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 28c8f53..855faaa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -17,11 +17,13 @@
package com.android.wm.shell.startingsurface;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.os.UserHandle.getUserHandleForUid;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -42,6 +44,7 @@
import com.android.internal.graphics.palette.Palette;
import com.android.internal.graphics.palette.Quantizer;
import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.TransactionPool;
import java.util.List;
@@ -62,6 +65,7 @@
// also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon.
private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f);
private final Context mContext;
+ private final IconProvider mIconProvider;
private final int mMaxAnimatableIconDuration;
private int mIconSize;
@@ -78,6 +82,7 @@
SplashscreenContentDrawer(Context context, int maxAnimatableIconDuration,
int iconExitAnimDuration, int appRevealAnimDuration, TransactionPool pool) {
mContext = context;
+ mIconProvider = new IconProvider(context);
mMaxAnimatableIconDuration = maxAnimatableIconDuration;
mAppRevealDuration = appRevealAnimDuration;
mIconExitDuration = iconExitAnimDuration;
@@ -141,20 +146,23 @@
}
}
- SplashScreenView makeSplashScreenContentView(Context context, int iconRes) {
+ SplashScreenView makeSplashScreenContentView(Context context, ActivityInfo ai) {
updateDensity();
getWindowAttrs(context, mTmpAttrs);
final StartingWindowViewBuilder builder = new StartingWindowViewBuilder();
final int animationDuration;
- final Drawable iconDrawable;
+ Drawable iconDrawable;
if (mTmpAttrs.mReplaceIcon != null) {
iconDrawable = mTmpAttrs.mReplaceIcon;
animationDuration = Math.max(0,
Math.min(mTmpAttrs.mAnimationDuration, mMaxAnimatableIconDuration));
} else {
- iconDrawable = iconRes != 0 ? context.getDrawable(iconRes)
- : context.getPackageManager().getDefaultActivityIcon();
+ iconDrawable = mIconProvider.getIconForUI(
+ ai, getUserHandleForUid(ai.applicationInfo.uid));
+ if (iconDrawable == null) {
+ iconDrawable = context.getPackageManager().getDefaultActivityIcon();
+ }
animationDuration = 0;
}
final int themeBGColor = peekWindowBGColor(context);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index b500813..1302314 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -126,6 +126,7 @@
labelRes = app.labelRes;
}
+ final int taskId = taskInfo.taskId;
Context context = mContext;
// replace with the default theme if the application didn't set
final int theme = windowInfo.splashScreenThemeResId != 0
@@ -153,6 +154,7 @@
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Failed creating package context with package name "
+ activityInfo.packageName + " for user " + taskInfo.userId, e);
+ return;
}
}
@@ -167,15 +169,21 @@
final TypedArray typedArray = overrideContext.obtainStyledAttributes(
com.android.internal.R.styleable.Window);
final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
- if (resId != 0 && overrideContext.getDrawable(resId) != null) {
- // We want to use the windowBackground for the override context if it is
- // available, otherwise we use the default one to make sure a themed starting
- // window is displayed for the app.
- if (DEBUG_SPLASH_SCREEN) {
- Slog.d(TAG, "addSplashScreen: apply overrideConfig"
- + taskConfig + " to starting window resId=" + resId);
+ try {
+ if (resId != 0 && overrideContext.getDrawable(resId) != null) {
+ // We want to use the windowBackground for the override context if it is
+ // available, otherwise we use the default one to make sure a themed starting
+ // window is displayed for the app.
+ if (DEBUG_SPLASH_SCREEN) {
+ Slog.d(TAG, "addSplashScreen: apply overrideConfig"
+ + taskConfig + " to starting window resId=" + resId);
+ }
+ context = overrideContext;
}
- context = overrideContext;
+ } catch (Resources.NotFoundException e) {
+ Slog.w(TAG, "failed creating starting window for overrideConfig at taskId: "
+ + taskId, e);
+ return;
}
typedArray.recycle();
}
@@ -258,7 +266,6 @@
params.setTitle("Splash Screen " + activityInfo.packageName);
// TODO(b/173975965) tracking performance
- final int taskId = taskInfo.taskId;
SplashScreenView sView = null;
try {
final View view = win.getDecorView();
@@ -270,7 +277,8 @@
if (splashscreenContentCompatible) {
win.setContentView(sView);
} else {
- sView = mSplashscreenContentDrawer.makeSplashScreenContentView(context, iconRes);
+ sView = mSplashscreenContentDrawer
+ .makeSplashScreenContentView(context, activityInfo);
win.setContentView(sView);
sView.cacheRootWindow(win);
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 35bab7a..fcd333f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -73,7 +73,8 @@
fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) {
assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName)
+ visibleRegion(primaryLayerName)
+ .coversExactly(getPrimaryRegion(dividerRegion, rotation))
}
}
@@ -83,7 +84,8 @@
) {
assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
- this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName)
+ visibleRegion(primaryLayerName)
+ .coversExactly(getPrimaryRegion(dividerRegion, rotation))
}
}
@@ -93,7 +95,8 @@
) {
assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName)
+ visibleRegion(secondaryLayerName)
+ .coversExactly(getSecondaryRegion(dividerRegion, rotation))
}
}
@@ -103,7 +106,8 @@
) {
assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
- this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName)
+ visibleRegion(secondaryLayerName)
+ .coversExactly(getSecondaryRegion(dividerRegion, rotation))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 63e9a78..614530b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -82,10 +82,10 @@
fun appsEndingBounds() {
testSpec.assertLayersEnd {
val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion),
- primaryApp.defaultWindowName)
- .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion),
- secondaryApp.defaultWindowName)
+ visibleRegion(primaryApp.defaultWindowName)
+ .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
+ visibleRegion(secondaryApp.defaultWindowName)
+ .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 234dda4..ef68ed6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -87,10 +87,10 @@
fun appsStartingBounds() {
testSpec.assertLayersStart {
val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion),
- primaryApp.defaultWindowName)
- coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion),
- secondaryApp.defaultWindowName)
+ visibleRegion(primaryApp.defaultWindowName)
+ .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
+ visibleRegion(secondaryApp.defaultWindowName)
+ .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index f40a08c..33ade38 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -183,8 +183,8 @@
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- this.coversExactly(topAppBounds, "SimpleActivity")
- .coversExactly(bottomAppBounds, "ImeActivity")
+ visibleRegion("SimpleActivity").coversExactly(topAppBounds)
+ visibleRegion("ImeActivity").coversExactly(bottomAppBounds)
}
}
@@ -203,8 +203,8 @@
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- this.coversExactly(topAppBounds, sSimpleActivity)
- .coversExactly(bottomAppBounds, sImeActivity)
+ visibleRegion(sSimpleActivity).coversExactly(topAppBounds)
+ visibleRegion(sImeActivity).coversExactly(bottomAppBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 0333227..1ba1f3b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -90,8 +90,8 @@
@Test
fun testAppCoversFullScreenWithPipOnDisplay() {
testSpec.assertLayersStart {
- coversExactly(displayBounds, testApp.defaultWindowName)
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ visibleRegion(testApp.defaultWindowName).coversExactly(displayBounds)
+ visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
}
}
@@ -99,7 +99,7 @@
@Test
fun pipAppCoversFullScreen() {
testSpec.assertLayersEnd {
- coversExactly(displayBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index ba88ee5..dd6195c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -116,7 +116,7 @@
@Test
fun pipAppLayerHidesTestApp() {
testSpec.assertLayersStart {
- coversExactly(startingBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversExactly(startingBounds)
isInvisible(testApp.defaultWindowName)
}
}
@@ -125,7 +125,7 @@
@Test
fun testAppLayerCoversFullScreen() {
testSpec.assertLayersEnd {
- coversExactly(endingBounds, testApp.defaultWindowName)
+ visibleRegion(testApp.defaultWindowName).coversExactly(endingBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index bf148bc..6166721 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -116,8 +116,8 @@
@Test
fun bothAppLayersVisible() {
testSpec.assertLayersEnd {
- coversAtMost(displayBounds, testApp.defaultWindowName)
- coversAtMost(displayBounds, imeApp.defaultWindowName)
+ visibleRegion(testApp.defaultWindowName).coversAtMost(displayBounds)
+ visibleRegion(imeApp.defaultWindowName).coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
index f554ca3..acc013a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
@@ -63,7 +63,7 @@
@Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 8ceef8a..852ee47 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -26,11 +26,11 @@
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -91,8 +91,8 @@
@Test
fun appLayerRotates_StartingBounds() {
testSpec.assertLayersStart {
- coversExactly(startingBounds, fixedApp.defaultWindowName)
- coversAtMost(startingBounds, pipApp.defaultWindowName)
+ visibleRegion(fixedApp.defaultWindowName).coversExactly(startingBounds)
+ visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
}
}
@@ -100,8 +100,8 @@
@Test
fun appLayerRotates_EndingBounds() {
testSpec.assertLayersEnd {
- coversExactly(endingBounds, fixedApp.defaultWindowName)
- coversAtMost(endingBounds, pipApp.defaultWindowName)
+ visibleRegion(fixedApp.defaultWindowName).coversExactly(endingBounds)
+ visibleRegion(pipApp.defaultWindowName).coversAtMost(endingBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 945a20b..6f17a2c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -83,7 +83,7 @@
@Test
fun testAppCoversFullScreen() {
testSpec.assertLayersStart {
- coversExactly(displayBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 102af92..9aab7f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -91,7 +91,7 @@
@Test
fun pipWindowInsideDisplay() {
testSpec.assertWmStart {
- coversAtMost(startingBounds, pipApp.defaultWindowName)
+ frameRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
}
}
@@ -107,7 +107,7 @@
@Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
- coversAtMost(startingBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
}
}
@@ -121,7 +121,7 @@
@Test
fun pipAppLayerCoversFullScreen() {
testSpec.assertLayersEnd {
- coversExactly(endingBounds, pipApp.defaultWindowName)
+ visibleRegion(pipApp.defaultWindowName).coversExactly(endingBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
index d6bcf03..3f47c04 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
@@ -22,6 +22,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -35,6 +36,7 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import org.junit.Before;
import org.junit.Test;
@@ -48,7 +50,8 @@
public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase {
private DisplayAreaInfo mDisplayAreaInfo;
private Display mDisplay;
- private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
+ private DisplayLayout mDisplayLayout;
+ private OneHandedBackgroundPanelOrganizer mSpiedBackgroundPanelOrganizer;
private WindowContainerToken mToken;
private SurfaceControl mLeash;
private TestableLooper mTestableLooper;
@@ -65,37 +68,38 @@
mToken = new WindowContainerToken(mMockRealToken);
mLeash = new SurfaceControl();
mDisplay = mContext.getDisplay();
+ mDisplayLayout = new DisplayLayout(mContext, mDisplay);
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
FEATURE_ONE_HANDED_BACKGROUND_PANEL);
- mBackgroundPanelOrganizer = new OneHandedBackgroundPanelOrganizer(mContext, mWindowManager,
- mMockDisplayController, Runnable::run);
+ mSpiedBackgroundPanelOrganizer = spy(
+ new OneHandedBackgroundPanelOrganizer(mContext, mDisplayLayout, Runnable::run));
}
@Test
public void testOnDisplayAreaAppeared() {
- mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
+ mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
mTestableLooper.processAllMessages();
- assertThat(mBackgroundPanelOrganizer.getBackgroundSurface()).isNotNull();
+ assertThat(mSpiedBackgroundPanelOrganizer.getBackgroundSurface()).isNotNull();
}
@Test
public void testShowBackgroundLayer() {
- mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
- mBackgroundPanelOrganizer.showBackgroundPanelLayer();
+ mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
+ mSpiedBackgroundPanelOrganizer.showBackgroundPanelLayer();
mTestableLooper.processAllMessages();
- assertThat(mBackgroundPanelOrganizer.mIsShowing).isTrue();
+ assertThat(mSpiedBackgroundPanelOrganizer.mIsShowing).isTrue();
}
@Test
public void testRemoveBackgroundLayer() {
- mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
- mBackgroundPanelOrganizer.removeBackgroundPanelLayer();
+ mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
+ mSpiedBackgroundPanelOrganizer.removeBackgroundPanelLayer();
mTestableLooper.processAllMessages();
- assertThat(mBackgroundPanelOrganizer.mIsShowing).isFalse();
+ assertThat(mSpiedBackgroundPanelOrganizer.mIsShowing).isFalse();
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index bd5fe2b..b0fc4c1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -23,20 +23,24 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.om.IOverlayManager;
+import android.graphics.Rect;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.util.ArrayMap;
import android.view.Display;
+import android.view.Surface;
import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -50,6 +54,7 @@
@RunWith(AndroidTestingRunner.class)
public class OneHandedControllerTest extends OneHandedTestCase {
Display mDisplay;
+ DisplayLayout mDisplayLayout;
OneHandedController mSpiedOneHandedController;
OneHandedTimeoutHandler mSpiedTimeoutHandler;
@@ -88,6 +93,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mDisplay = mContext.getDisplay();
+ mDisplayLayout = new DisplayLayout(mContext, mDisplay);
mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor));
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
@@ -103,9 +109,12 @@
when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any())).thenReturn(
mDefaultSwipeToNotificationEnabled);
+ when(mMockDisplayAreaOrganizer.getLastDisplayBounds()).thenReturn(
+ new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height()));
+ when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mDisplayLayout);
+
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
- mWindowManager,
mMockDisplayController,
mMockBackgroundOrganizer,
mMockDisplayAreaOrganizer,
@@ -127,7 +136,7 @@
final OneHandedAnimationController animationController = new OneHandedAnimationController(
mContext);
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
- mContext, mWindowManager, animationController, mMockTutorialHandler,
+ mContext, mDisplayLayout, animationController, mMockTutorialHandler,
mMockBackgroundOrganizer, mMockShellMainExecutor);
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
@@ -188,7 +197,7 @@
mSpiedOneHandedController.setOneHandedEnabled(true);
verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
- verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
+ verify(mMockGestureHandler, atLeastOnce()).onGestureEnabled(anyBoolean());
}
@Test
@@ -196,7 +205,7 @@
mSpiedOneHandedController.setSwipeToNotificationEnabled(mDefaultSwipeToNotificationEnabled);
verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
- verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean());
+ verify(mMockGestureHandler, atLeastOnce()).onGestureEnabled(anyBoolean());
}
@Test
@@ -282,4 +291,67 @@
verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt());
}
+
+ @Test
+ public void testRotation90CanNotStartOneHanded() {
+ final DisplayLayout landscapeDisplayLayout = new DisplayLayout(mDisplayLayout);
+ landscapeDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+ when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
+ when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(landscapeDisplayLayout);
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+ mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
+ mSpiedOneHandedController.startOneHanded();
+
+ verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt());
+ }
+
+ @Test
+ public void testRotation180CanStartOneHanded() {
+ final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout);
+ testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
+ when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
+ when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout);
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+ mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
+ mSpiedOneHandedController.startOneHanded();
+
+ verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt());
+ }
+
+ @Test
+ public void testRotation270CanNotStartOneHanded() {
+ final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout);
+ testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
+ when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
+ when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout);
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+ mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
+ mSpiedOneHandedController.startOneHanded();
+
+ verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt());
+ }
+
+ @Test
+ public void testDisabled3ButtonGestureWhenKeyguardOn() {
+ final boolean isOneHandedEnabled = true;
+ final boolean isLockWhenKeyguardOn = true;
+ final boolean isEnabledWhenKeyguardOn = false;
+ mSpiedOneHandedController.setOneHandedEnabled(isOneHandedEnabled);
+ mSpiedOneHandedController.setLockedDisabled(isLockWhenKeyguardOn, isEnabledWhenKeyguardOn);
+
+ verify(mMockGestureHandler).onGestureEnabled(isEnabledWhenKeyguardOn);
+ }
+
+ @Test
+ public void testEnabled3ButtonGestureWhenKeyguardGoingAway() {
+ final boolean isOneHandedEnabled = true;
+ final boolean isLockWhenKeyguardOn = false;
+ final boolean isEnabledWhenKeyguardOn = false;
+ mSpiedOneHandedController.setOneHandedEnabled(isOneHandedEnabled);
+ reset(mMockGestureHandler);
+
+ mSpiedOneHandedController.setLockedDisabled(isLockWhenKeyguardOn, isEnabledWhenKeyguardOn);
+
+ verify(mMockGestureHandler).onGestureEnabled(isOneHandedEnabled);
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index f897b09..f654bb5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -19,6 +19,9 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
+import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
+import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -32,6 +35,7 @@
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Binder;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -47,6 +51,7 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
@@ -67,6 +72,7 @@
DisplayAreaInfo mDisplayAreaInfo;
Display mDisplay;
+ DisplayLayout mDisplayLayout;
OneHandedDisplayAreaOrganizer mSpiedDisplayAreaOrganizer;
OneHandedTutorialHandler mTutorialHandler;
OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
@@ -103,6 +109,7 @@
mToken = new WindowContainerToken(mMockRealToken);
mLeash = new SurfaceControl();
mDisplay = mContext.getDisplay();
+ mDisplayLayout = new DisplayLayout(mContext, mDisplay);
mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, FEATURE_ONE_HANDED);
mDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_PORTRAIT;
when(mMockAnimationController.getAnimator(any(), any(), any(), any())).thenReturn(null);
@@ -121,7 +128,7 @@
when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
mSpiedDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext,
- mWindowManager,
+ mDisplayLayout,
mMockAnimationController,
mTutorialHandler,
mMockBackgroundOrganizer,
@@ -169,20 +176,13 @@
}
@Test
- public void testRotation_getNewDisplayBounds() {
- when(mMockLeash.isValid()).thenReturn(false);
- // Rotate 0 -> 90
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90,
- mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).getDisplayBounds();
- }
-
- @Test
public void testRotation_portrait_0_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 90
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_90,
mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -190,8 +190,10 @@
public void testRotation_portrait_0_to_seascape_270() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 270
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_270,
mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -199,8 +201,12 @@
public void testRotation_portrait_180_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 180 -> 90
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_90,
mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -208,8 +214,12 @@
public void testRotation_portrait_180_to_seascape_270() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 180 -> 270
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_270,
mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -217,8 +227,12 @@
public void testRotation_landscape_90_to_portrait_0() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 90 -> 0
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_0,
mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -226,26 +240,38 @@
public void testRotation_landscape_90_to_portrait_180() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 90 -> 180
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_180,
mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
- public void testRotation_Seascape_270_to_portrait_0() {
+ public void testRotation_seascape_270_to_portrait_0() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 270 -> 0
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_0,
mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
- public void testRotation_seascape_90_to_portrait_180() {
+ public void testRotation_seascape_270_to_portrait_180() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 270 -> 180
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_180,
mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -253,7 +279,10 @@
public void testRotation_portrait_0_to_portrait_0() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 0
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_0,
+ mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset(
mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@@ -262,16 +291,23 @@
public void testRotation_portrait_0_to_portrait_180() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 180
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_portrait_180_to_portrait_180() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 180 -> 180
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_180,
+ mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset(
mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@@ -280,16 +316,25 @@
public void testRotation_portrait_180_to_portrait_0() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 180 -> 0
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_landscape_90_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 90 -> 90
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_90,
+ mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset(
mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@@ -298,26 +343,60 @@
public void testRotation_landscape_90_to_seascape_270() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 90 -> 270
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_seascape_270_to_seascape_270() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 270 -> 270
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_270,
+ mMockWindowContainerTransaction);
+
+ verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset(
mMockWindowContainerTransaction);
verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
- public void testRotation_seascape_90_to_landscape_90() {
+ public void testRotation_seascape_270_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 270 -> 90
- mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90,
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(
+ mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
+ }
+
+ @Test
+ public void testTriggerOffset() {
+ final Rect testBounds = mSpiedDisplayAreaOrganizer.getLastDisplayBounds();
+ final int offset = 100;
+ testBounds.offsetTo(0, offset);
+ mSpiedDisplayAreaOrganizer.finishOffset(offset, TRANSITION_DIRECTION_TRIGGER);
+
+ assertThat(mSpiedDisplayAreaOrganizer.getLastDisplayBounds()).isEqualTo(testBounds);
+ }
+
+ @Test
+ public void testExitOffsetToZero() {
+ final Rect testBounds = mSpiedDisplayAreaOrganizer.getLastDisplayBounds();
+ final int offset = 100;
+ mSpiedDisplayAreaOrganizer.finishOffset(offset, TRANSITION_DIRECTION_TRIGGER);
+ mSpiedDisplayAreaOrganizer.finishOffset(0, TRANSITION_DIRECTION_EXIT);
+
+ assertThat(mSpiedDisplayAreaOrganizer.getLastDisplayBounds()).isEqualTo(testBounds);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
index f683e4a..5d82a70 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
@@ -16,21 +16,19 @@
package com.android.wm.shell.onehanded;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.view.Surface;
import android.view.ViewConfiguration;
-import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -44,24 +42,20 @@
@RunWith(AndroidTestingRunner.class)
public class OneHandedGestureHandlerTest extends OneHandedTestCase {
OneHandedGestureHandler mGestureHandler;
- @Mock
- DisplayController mMockDisplayController;
+ DisplayLayout mDisplayLayout;
@Mock
DisplayLayout mMockDisplayLayout;
@Mock
ShellExecutor mMockShellMainExecutor;
- @Mock
- WindowContainerTransaction mMockWct;
@Before
public void setUp() {
final int mockNavBarHeight = 100;
MockitoAnnotations.initMocks(this);
- mGestureHandler = new OneHandedGestureHandler(mContext, mWindowManager,
- mMockDisplayController, ViewConfiguration.get(mTestContext),
- mMockShellMainExecutor);
+ mDisplayLayout = new DisplayLayout(mContext, mContext.getDisplay());
+ mGestureHandler = new OneHandedGestureHandler(mContext, mDisplayLayout,
+ ViewConfiguration.get(mTestContext), mMockShellMainExecutor);
when(mMockDisplayLayout.navBarFrameHeight()).thenReturn(mockNavBarHeight);
- when(mMockDisplayController.getDisplayLayout(anyInt())).thenReturn(mMockDisplayLayout);
}
@Test
@@ -81,7 +75,7 @@
@Test
public void testOneHandedDisabled_shouldDisposeInputChannel() {
- mGestureHandler.onOneHandedEnabled(false);
+ mGestureHandler.onGestureEnabled(false);
assertThat(mGestureHandler.mInputMonitor).isNull();
assertThat(mGestureHandler.mInputEventReceiver).isNull();
@@ -89,7 +83,7 @@
@Test
public void testChangeNavBarToNon3Button_shouldDisposeInputChannel() {
- mGestureHandler.onOneHandedEnabled(true);
+ mGestureHandler.onGestureEnabled(true);
mGestureHandler.onThreeButtonModeEnabled(false);
assertThat(mGestureHandler.mInputMonitor).isNull();
@@ -98,11 +92,38 @@
@Test
public void testOnlyHandleGestureInPortraitMode() {
- mGestureHandler.onOneHandedEnabled(true);
- mGestureHandler.onRotateDisplay(DEFAULT_DISPLAY, Surface.ROTATION_0, Surface.ROTATION_90,
- mMockWct);
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+ mGestureHandler.onGestureEnabled(true);
+ mGestureHandler.onRotateDisplay(mDisplayLayout);
assertThat(mGestureHandler.mInputMonitor).isNull();
assertThat(mGestureHandler.mInputEventReceiver).isNull();
}
+
+ @Test
+ public void testRotation90ShouldNotRegisterEventReceiver() throws InterruptedException {
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+ mGestureHandler.onGestureEnabled(true);
+ mGestureHandler.onRotateDisplay(mDisplayLayout);
+
+ verify(mMockShellMainExecutor, never()).executeBlocking(any());
+ }
+
+ @Test
+ public void testRotation180ShouldNotRegisterEventReceiver() throws InterruptedException {
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
+ mGestureHandler.onGestureEnabled(true);
+ mGestureHandler.onRotateDisplay(mDisplayLayout);
+
+ verify(mMockShellMainExecutor, never()).executeBlocking(any());
+ }
+
+ @Test
+ public void testRotation270ShouldNotRegisterEventReceiver() throws InterruptedException {
+ mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
+ mGestureHandler.onGestureEnabled(true);
+ mGestureHandler.onRotateDisplay(mDisplayLayout);
+
+ verify(mMockShellMainExecutor, never()).executeBlocking(any());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index f586dda..2886bb1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -75,7 +75,6 @@
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
mOneHandedController = new OneHandedController(
mContext,
- mWindowManager,
mMockDisplayController,
mMockBackgroundOrganizer,
mMockDisplayAreaOrganizer,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 8ae0a73..78b3d4e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -112,7 +112,8 @@
spyOn(context);
spyOn(realWindowManager);
try {
- doReturn(context).when(context).createPackageContext(anyString(), anyInt());
+ doReturn(context).when(context)
+ .createPackageContextAsUser(anyString(), anyInt(), any());
} catch (PackageManager.NameNotFoundException e) {
//
}
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 168a863..1e90b7c 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1379,6 +1379,11 @@
enum : uint32_t {
// Additional flag indicating an entry is public.
SPEC_PUBLIC = 0x40000000u,
+
+ // Additional flag indicating the resource id for this resource may change in a future
+ // build. If this flag is set, the SPEC_PUBLIC flag is also set since the resource must be
+ // public to be exposed as an API to other applications.
+ SPEC_STAGED_API = 0x20000000u,
};
};
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 42aa87b..ea9cbd5 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -162,6 +162,12 @@
*/
#define PROPERTY_IS_EMULATOR "ro.boot.qemu"
+/**
+ * Turns on the Skia GPU option "reduceOpsTaskSplitting" which improves GPU
+ * efficiency but may increase VRAM consumption. Default is "false".
+ */
+#define PROPERTY_REDUCE_OPS_TASK_SPLITTING "renderthread.skia.reduceopstasksplitting"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 5dc02e8..adf4aee 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -20,6 +20,7 @@
#include "CanvasContext.h"
#include "DeviceInfo.h"
#include "EglManager.h"
+#include "Properties.h"
#include "Readback.h"
#include "RenderProxy.h"
#include "VulkanManager.h"
@@ -40,6 +41,7 @@
#include <utils/Mutex.h>
#include <thread>
+#include <android-base/properties.h>
#include <ui/FatVector.h>
namespace android {
@@ -251,6 +253,11 @@
void RenderThread::initGrContextOptions(GrContextOptions& options) {
options.fPreferExternalImagesOverES3 = true;
options.fDisableDistanceFieldPaths = true;
+ if (android::base::GetBoolProperty(PROPERTY_REDUCE_OPS_TASK_SPLITTING, false)) {
+ options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kYes;
+ } else {
+ options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo;
+ }
}
void RenderThread::destroyRenderingContext() {
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 5e2e559..5f8d795 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -1246,20 +1246,42 @@
* Returns true if the Location came from a mock provider.
*
* @return true if this Location came from a mock provider, false otherwise
+ * @deprecated Prefer {@link #isMock()} instead.
*/
+ @Deprecated
public boolean isFromMockProvider() {
- return (mFieldsMask & HAS_MOCK_PROVIDER_MASK) != 0;
+ return isMock();
}
/**
* Flag this Location as having come from a mock provider or not.
*
* @param isFromMockProvider true if this Location came from a mock provider, false otherwise
+ * @deprecated Prefer {@link #setMock(boolean)} instead.
* @hide
*/
+ @Deprecated
@SystemApi
public void setIsFromMockProvider(boolean isFromMockProvider) {
- if (isFromMockProvider) {
+ setMock(isFromMockProvider);
+ }
+
+ /**
+ * Returns true if this location is marked as a mock location. If this location comes from the
+ * Android framework, this indicates that the location was provided by a test location provider,
+ * and thus may not be related to the actual location of the device.
+ *
+ * @see LocationManager#addTestProvider
+ */
+ public boolean isMock() {
+ return (mFieldsMask & HAS_MOCK_PROVIDER_MASK) != 0;
+ }
+
+ /**
+ * Sets whether this location is marked as a mock location.
+ */
+ public void setMock(boolean mock) {
+ if (mock) {
mFieldsMask |= HAS_MOCK_PROVIDER_MASK;
} else {
mFieldsMask &= ~HAS_MOCK_PROVIDER_MASK;
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index e73e915..f1879fc 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -346,43 +346,6 @@
*/
public static final String EXTRA_GNSS_CAPABILITIES = "android.location.extra.GNSS_CAPABILITIES";
- /**
- * Broadcast intent action for Settings app to inject a footer at the bottom of location
- * settings. This is for use only by apps that are included in the system image.
- *
- * <p>To inject a footer to location settings, you must declare a broadcast receiver for
- * this action in the manifest:
- * <pre>
- * <receiver android:name="com.example.android.footer.MyFooterInjector">
- * <intent-filter>
- * <action android:name="com.android.settings.location.INJECT_FOOTER" />
- * </intent-filter>
- * <meta-data
- * android:name="com.android.settings.location.FOOTER_STRING"
- * android:resource="@string/my_injected_footer_string" />
- * </receiver>
- * </pre>
- *
- * <p>This broadcast receiver will never actually be invoked. See also
- * {#METADATA_SETTINGS_FOOTER_STRING}.
- *
- * @hide
- */
- public static final String SETTINGS_FOOTER_DISPLAYED_ACTION =
- "com.android.settings.location.DISPLAYED_FOOTER";
-
- /**
- * Metadata name for {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION} broadcast
- * receivers to specify a string resource id as location settings footer text. This is for use
- * only by apps that are included in the system image.
- *
- * <p>See {@link #SETTINGS_FOOTER_DISPLAYED_ACTION} for more detail on how to use.
- *
- * @hide
- */
- public static final String METADATA_SETTINGS_FOOTER_STRING =
- "com.android.settings.location.FOOTER_STRING";
-
private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY =
diff --git a/location/java/android/location/SatellitePvt.java b/location/java/android/location/SatellitePvt.java
index 144fa13..27c7eda 100644
--- a/location/java/android/location/SatellitePvt.java
+++ b/location/java/android/location/SatellitePvt.java
@@ -25,8 +25,9 @@
/**
* A class that contains GNSS satellite position, velocity and time information at the
- * signal transmission time {@link GnssMeasurement#getReceivedSvTimeNanos()}.
+ * same signal transmission time {@link GnssMeasurement#getReceivedSvTimeNanos()}.
*
+ * <p>The position and velocity must be in ECEF coordinates.
* @hide
*/
@SystemApi
@@ -39,6 +40,9 @@
/**
* Class containing estimates of the satellite position fields in ECEF coordinate frame.
+ *
+ * <p>The satellite position must be defined at the time of transmission of the signal
+ * receivedSvTimeNs.
*/
public static final class PositionEcef implements Parcelable {
private final double mXMeters;
@@ -133,6 +137,9 @@
/**
* Class containing estimates of the satellite velocity fields in the ECEF coordinate frame.
+ *
+ * <p>The satellite velocity must be defined at the time of transmission of the signal
+ * receivedSvTimeNs.
*/
public static final class VelocityEcef implements Parcelable {
private final double mXMetersPerSecond;
diff --git a/media/java/android/media/AudioDescriptor.java b/media/java/android/media/AudioDescriptor.java
new file mode 100644
index 0000000..11371b1
--- /dev/null
+++ b/media/java/android/media/AudioDescriptor.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The AudioDescriptor contains the information to describe the audio playback/capture
+ * capabilities. The capabilities are described by a byte array, which is defined by a
+ * particular standard. This is used when the format is unrecognized to the platform.
+ */
+public class AudioDescriptor {
+ /**
+ * The audio standard is not specified.
+ */
+ public static final int STANDARD_NONE = 0;
+ /**
+ * The Extended Display Identification Data (EDID) standard for a short audio descriptor.
+ */
+ public static final int STANDARD_EDID = 1;
+
+ /** @hide */
+ @IntDef({
+ STANDARD_NONE,
+ STANDARD_EDID,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AudioDescriptorStandard {}
+
+ private final int mStandard;
+ private final byte[] mDescriptor;
+ private final int mEncapsulationType;
+
+ AudioDescriptor(int standard, int encapsulationType, @NonNull byte[] descriptor) {
+ mStandard = standard;
+ mEncapsulationType = encapsulationType;
+ mDescriptor = descriptor;
+ }
+
+ /**
+ * @return the standard that defines audio playback/capture capabilities.
+ */
+ public @AudioDescriptorStandard int getStandard() {
+ return mStandard;
+ }
+
+ /**
+ * @return a byte array that describes audio playback/capture capabilities as encoded by the
+ * standard for this AudioDescriptor.
+ */
+ public @NonNull byte[] getDescriptor() {
+ return mDescriptor;
+ }
+
+ /**
+ * The encapsulation type indicates what encapsulation type is required when the framework is
+ * using this extra audio descriptor for playing to a device exposing this audio profile.
+ * When encapsulation is required, only playback with {@link android.media.AudioTrack} API is
+ * supported. But playback with {@link android.media.MediaPlayer} is not.
+ * When an encapsulation type is required, the {@link AudioFormat} encoding selected when
+ * creating the {@link AudioTrack} must match the encapsulation type, e.g
+ * AudioFormat#ENCODING_IEC61937 for AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937.
+ *
+ * @return an integer representing the encapsulation type
+ *
+ * @see AudioProfile#AUDIO_ENCAPSULATION_TYPE_NONE
+ * @see AudioProfile#AUDIO_ENCAPSULATION_TYPE_IEC61937
+ */
+ public @AudioProfile.EncapsulationType int getEncapsulationType() {
+ return mEncapsulationType;
+ }
+}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 383c93d..9300f13 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -519,6 +519,13 @@
}
/**
+ * @return A list of {@link AudioDescriptor} supported by the audio devices.
+ */
+ public @NonNull List<AudioDescriptor> getAudioDescriptors() {
+ return mPort.audioDescriptors();
+ }
+
+ /**
* Returns an array of supported encapsulation modes for the device.
*
* The array can include any of the {@code AudioTrack} encapsulation modes,
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index 9c42bf7..ebe0882 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -60,10 +60,11 @@
AudioDevicePort(AudioHandle handle, String deviceName, List<AudioProfile> profiles,
AudioGain[] gains, int type, String address, int[] encapsulationModes,
- @AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes) {
+ @AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes,
+ List<AudioDescriptor> descriptors) {
super(handle,
AudioManager.isInputDevice(type) ? AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK,
- deviceName, profiles, gains);
+ deviceName, profiles, gains, descriptors);
mType = type;
mAddress = address;
mEncapsulationModes = encapsulationModes;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9aeef07..a717a90 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3221,6 +3221,23 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final int NUM_SOUND_EFFECTS = 16;
+ /** @hide */
+ @IntDef(prefix = { "FX_" }, value = {
+ FX_KEY_CLICK,
+ FX_FOCUS_NAVIGATION_UP,
+ FX_FOCUS_NAVIGATION_DOWN,
+ FX_FOCUS_NAVIGATION_LEFT,
+ FX_FOCUS_NAVIGATION_RIGHT,
+ FX_KEYPRESS_STANDARD,
+ FX_KEYPRESS_SPACEBAR,
+ FX_KEYPRESS_DELETE,
+ FX_KEYPRESS_RETURN,
+ FX_KEYPRESS_INVALID,
+ FX_BACK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemSoundEffect {}
+
/**
* @hide Number of FX_FOCUS_NAVIGATION_REPEAT_* sound effects
*/
@@ -3296,22 +3313,11 @@
/**
* Plays a sound effect (Key clicks, lid open/close...)
- * @param effectType The type of sound effect. One of
- * {@link #FX_KEY_CLICK},
- * {@link #FX_FOCUS_NAVIGATION_UP},
- * {@link #FX_FOCUS_NAVIGATION_DOWN},
- * {@link #FX_FOCUS_NAVIGATION_LEFT},
- * {@link #FX_FOCUS_NAVIGATION_RIGHT},
- * {@link #FX_KEYPRESS_STANDARD},
- * {@link #FX_KEYPRESS_SPACEBAR},
- * {@link #FX_KEYPRESS_DELETE},
- * {@link #FX_KEYPRESS_RETURN},
- * {@link #FX_KEYPRESS_INVALID},
- * {@link #FX_BACK},
+ * @param effectType The type of sound effect.
* NOTE: This version uses the UI settings to determine
* whether sounds are heard or not.
*/
- public void playSoundEffect(int effectType) {
+ public void playSoundEffect(@SystemSoundEffect int effectType) {
if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
return;
}
@@ -3330,24 +3336,13 @@
/**
* Plays a sound effect (Key clicks, lid open/close...)
- * @param effectType The type of sound effect. One of
- * {@link #FX_KEY_CLICK},
- * {@link #FX_FOCUS_NAVIGATION_UP},
- * {@link #FX_FOCUS_NAVIGATION_DOWN},
- * {@link #FX_FOCUS_NAVIGATION_LEFT},
- * {@link #FX_FOCUS_NAVIGATION_RIGHT},
- * {@link #FX_KEYPRESS_STANDARD},
- * {@link #FX_KEYPRESS_SPACEBAR},
- * {@link #FX_KEYPRESS_DELETE},
- * {@link #FX_KEYPRESS_RETURN},
- * {@link #FX_KEYPRESS_INVALID},
- * {@link #FX_BACK},
+ * @param effectType The type of sound effect.
* @param userId The current user to pull sound settings from
* NOTE: This version uses the UI settings to determine
* whether sounds are heard or not.
* @hide
*/
- public void playSoundEffect(int effectType, int userId) {
+ public void playSoundEffect(@SystemSoundEffect int effectType, int userId) {
if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
return;
}
@@ -3366,25 +3361,14 @@
/**
* Plays a sound effect (Key clicks, lid open/close...)
- * @param effectType The type of sound effect. One of
- * {@link #FX_KEY_CLICK},
- * {@link #FX_FOCUS_NAVIGATION_UP},
- * {@link #FX_FOCUS_NAVIGATION_DOWN},
- * {@link #FX_FOCUS_NAVIGATION_LEFT},
- * {@link #FX_FOCUS_NAVIGATION_RIGHT},
- * {@link #FX_KEYPRESS_STANDARD},
- * {@link #FX_KEYPRESS_SPACEBAR},
- * {@link #FX_KEYPRESS_DELETE},
- * {@link #FX_KEYPRESS_RETURN},
- * {@link #FX_KEYPRESS_INVALID},
- * {@link #FX_BACK},
+ * @param effectType The type of sound effect.
* @param volume Sound effect volume.
* The volume value is a raw scalar so UI controls should be scaled logarithmically.
* If a volume of -1 is specified, the AudioManager.STREAM_MUSIC stream volume minus 3dB will be used.
* NOTE: This version is for applications that have their own
* settings panel for enabling and controlling volume.
*/
- public void playSoundEffect(int effectType, float volume) {
+ public void playSoundEffect(@SystemSoundEffect int effectType, float volume) {
if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
return;
}
@@ -3827,6 +3811,14 @@
*/
@SystemApi
public static final int AUDIOFOCUS_FLAG_LOCK = 0x1 << 2;
+
+ /**
+ * @hide
+ * flag set on test API calls,
+ * see {@link #requestAudioFocusForTest(AudioFocusRequest, String, int, int)},
+ * note that it isn't used in conjunction with other flags, it is passed as the single
+ * value for flags */
+ public static final int AUDIOFOCUS_FLAG_TEST = 0x1 << 3;
/** @hide */
public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK
| AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS;
@@ -3988,6 +3980,76 @@
/**
* @hide
+ * Test API to request audio focus for an arbitrary client operating from a (fake) given UID.
+ * Used to simulate conditions of the test, not the behavior of the focus requester under test.
+ * @param afr the parameters of the request
+ * @param clientFakeId the identifier of the AudioManager the client would be requesting from
+ * @param clientFakeUid the UID of the client, here an arbitrary int,
+ * doesn't have to be a real UID
+ * @param clientTargetSdk the target SDK used by the client
+ * @return return code indicating status of the request
+ */
+ @TestApi
+ @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
+ public @FocusRequestResult int requestAudioFocusForTest(@NonNull AudioFocusRequest afr,
+ @NonNull String clientFakeId, int clientFakeUid, int clientTargetSdk) {
+ Objects.requireNonNull(afr);
+ Objects.requireNonNull(clientFakeId);
+ try {
+ return getService().requestAudioFocusForTest(afr.getAudioAttributes(),
+ afr.getFocusGain(),
+ mICallBack,
+ mAudioFocusDispatcher,
+ clientFakeId, "com.android.test.fakeclient", clientFakeUid, clientTargetSdk);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Test API to abandon audio focus for an arbitrary client.
+ * Used to simulate conditions of the test, not the behavior of the focus requester under test.
+ * @param afr the parameters used for the request
+ * @param clientFakeId clientFakeId the identifier of the AudioManager from which the client
+ * would be requesting
+ * @return return code indicating status of the request
+ */
+ @TestApi
+ @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
+ public @FocusRequestResult int abandonAudioFocusForTest(@NonNull AudioFocusRequest afr,
+ @NonNull String clientFakeId) {
+ Objects.requireNonNull(afr);
+ Objects.requireNonNull(clientFakeId);
+ try {
+ return getService().abandonAudioFocusForTest(mAudioFocusDispatcher,
+ clientFakeId, afr.getAudioAttributes(), "com.android.test.fakeclient");
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Return the duration of the fade out applied when a player of the given AudioAttributes
+ * is losing audio focus
+ * @param aa the AudioAttributes of the player losing focus with {@link #AUDIOFOCUS_LOSS}
+ * @return a duration in ms, 0 indicates no fade out is applied
+ */
+ @TestApi
+ @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
+ public @IntRange(from = 0) long getFadeOutDurationOnFocusLossMillis(@NonNull AudioAttributes aa)
+ {
+ Objects.requireNonNull(aa);
+ try {
+ return getService().getFadeOutDurationOnFocusLossMillis(aa);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
* Request or lock audio focus.
* This method is to be used by system components that have registered an
* {@link android.media.audiopolicy.AudioPolicy} to request audio focus, but also to "lock" it
diff --git a/media/java/android/media/AudioMixPort.java b/media/java/android/media/AudioMixPort.java
index 7f2249d..b24268a 100644
--- a/media/java/android/media/AudioMixPort.java
+++ b/media/java/android/media/AudioMixPort.java
@@ -46,7 +46,7 @@
AudioMixPort(AudioHandle handle, int ioHandle, int role, String deviceName,
List<AudioProfile> profiles, AudioGain[] gains) {
- super(handle, role, deviceName, profiles, gains);
+ super(handle, role, deviceName, profiles, gains, null);
mIoHandle = ioHandle;
}
diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java
index 0302250..8a2d096 100644
--- a/media/java/android/media/AudioPort.java
+++ b/media/java/android/media/AudioPort.java
@@ -86,6 +86,7 @@
private final int[] mChannelIndexMasks;
private final int[] mFormats;
private final List<AudioProfile> mProfiles;
+ private final List<AudioDescriptor> mDescriptors;
@UnsupportedAppUsage
private final AudioGain[] mGains;
@UnsupportedAppUsage
@@ -107,17 +108,21 @@
if (mFormats != null) {
for (int format : mFormats) {
mProfiles.add(new AudioProfile(
- format, samplingRates, channelMasks, channelIndexMasks));
+ format, samplingRates, channelMasks, channelIndexMasks,
+ AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE));
}
}
+ mDescriptors = new ArrayList<>();
}
AudioPort(AudioHandle handle, int role, String name,
- List<AudioProfile> profiles, AudioGain[] gains) {
+ List<AudioProfile> profiles, AudioGain[] gains,
+ List<AudioDescriptor> descriptors) {
mHandle = handle;
mRole = role;
mName = name;
mProfiles = profiles;
+ mDescriptors = descriptors;
mGains = gains;
Set<Integer> formats = new HashSet<>();
Set<Integer> samplingRates = new HashSet<>();
@@ -210,6 +215,13 @@
}
/**
+ * Get the list of audio descriptor
+ */
+ public List<AudioDescriptor> audioDescriptors() {
+ return mDescriptors;
+ }
+
+ /**
* Get the list of gain descriptors
* Empty array if this port does not have gain control
*/
diff --git a/media/java/android/media/AudioProfile.java b/media/java/android/media/AudioProfile.java
index ac96e6f..ae8d0a5 100644
--- a/media/java/android/media/AudioProfile.java
+++ b/media/java/android/media/AudioProfile.java
@@ -16,27 +16,55 @@
package android.media;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.stream.Collectors;
/**
* An AudioProfile is specific to an audio format and lists supported sampling rates and
* channel masks for that format. An {@link AudioDeviceInfo} has a list of supported AudioProfiles.
+ * There can be multiple profiles whose encoding format is the same. This usually happens when
+ * an encoding format is only supported when it is encapsulated by some particular encapsulation
+ * types. If there are multiple encapsulation types that can carry this encoding format, they will
+ * be reported in different audio profiles. The application can choose any of the encapsulation
+ * types.
*/
public class AudioProfile {
+ /**
+ * No encapsulation type is specified.
+ */
+ public static final int AUDIO_ENCAPSULATION_TYPE_NONE = 0;
+ /**
+ * Encapsulation format is defined in standard IEC 61937.
+ */
+ public static final int AUDIO_ENCAPSULATION_TYPE_IEC61937 = 1;
+
+ /** @hide */
+ @IntDef({
+ AUDIO_ENCAPSULATION_TYPE_NONE,
+ AUDIO_ENCAPSULATION_TYPE_IEC61937,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EncapsulationType {}
+
private final int mFormat;
private final int[] mSamplingRates;
private final int[] mChannelMasks;
private final int[] mChannelIndexMasks;
+ private final int mEncapsulationType;
AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks,
- @NonNull int[] channelIndexMasks) {
+ @NonNull int[] channelIndexMasks,
+ int encapsulationType) {
mFormat = format;
mSamplingRates = samplingRates;
mChannelMasks = channelMasks;
mChannelIndexMasks = channelIndexMasks;
+ mEncapsulationType = encapsulationType;
}
/**
@@ -67,6 +95,24 @@
return mSamplingRates;
}
+ /**
+ * The encapsulation type indicates what encapsulation type is required when the framework is
+ * using this format when playing to a device exposing this audio profile.
+ * When encapsulation is required, only playback with {@link android.media.AudioTrack} API is
+ * supported. But playback with {@link android.media.MediaPlayer} is not.
+ * When an encapsulation type is required, the {@link AudioFormat} encoding selected when
+ * creating the {@link AudioTrack} must match the encapsulation type, e.g
+ * AudioFormat.ENCODING_IEC61937 for AUDIO_ENCAPSULATION_TYPE_IEC61937.
+ *
+ * @return an integer representing the encapsulation type
+ *
+ * @see #AUDIO_ENCAPSULATION_TYPE_NONE
+ * @see #AUDIO_ENCAPSULATION_TYPE_IEC61937
+ */
+ public @EncapsulationType int getEncapsulationType() {
+ return mEncapsulationType;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 9c6b276..2059f02 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -16,6 +16,9 @@
package android.media;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
@@ -23,6 +26,9 @@
import android.hardware.camera2.CameraMetadata;
import android.os.Build;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Retrieves the
* predefined camcorder profile settings for camcorder applications.
@@ -276,6 +282,53 @@
private static final int QUALITY_HIGH_SPEED_LIST_END = QUALITY_HIGH_SPEED_4KDCI;
/**
+ * @hide
+ */
+ @IntDef({
+ QUALITY_LOW,
+ QUALITY_HIGH,
+ QUALITY_QCIF,
+ QUALITY_CIF,
+ QUALITY_480P,
+ QUALITY_720P,
+ QUALITY_1080P,
+ QUALITY_QVGA,
+ QUALITY_2160P,
+ QUALITY_VGA,
+ QUALITY_4KDCI,
+ QUALITY_QHD,
+ QUALITY_2K,
+ QUALITY_8KUHD,
+
+ QUALITY_TIME_LAPSE_LOW ,
+ QUALITY_TIME_LAPSE_HIGH,
+ QUALITY_TIME_LAPSE_QCIF,
+ QUALITY_TIME_LAPSE_CIF,
+ QUALITY_TIME_LAPSE_480P,
+ QUALITY_TIME_LAPSE_720P,
+ QUALITY_TIME_LAPSE_1080P,
+ QUALITY_TIME_LAPSE_QVGA,
+ QUALITY_TIME_LAPSE_2160P,
+ QUALITY_TIME_LAPSE_VGA,
+ QUALITY_TIME_LAPSE_4KDCI,
+ QUALITY_TIME_LAPSE_QHD,
+ QUALITY_TIME_LAPSE_2K,
+ QUALITY_TIME_LAPSE_8KUHD,
+
+ QUALITY_HIGH_SPEED_LOW,
+ QUALITY_HIGH_SPEED_HIGH,
+ QUALITY_HIGH_SPEED_480P,
+ QUALITY_HIGH_SPEED_720P,
+ QUALITY_HIGH_SPEED_1080P,
+ QUALITY_HIGH_SPEED_2160P,
+ QUALITY_HIGH_SPEED_CIF,
+ QUALITY_HIGH_SPEED_VGA,
+ QUALITY_HIGH_SPEED_4KDCI,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Quality {}
+
+ /**
* Default recording duration in seconds before the session is terminated.
* This is useful for applications like MMS has limited file size requirement.
*/
@@ -385,9 +438,8 @@
public int audioChannels;
/**
- * Returns the camcorder profile for the first back-facing camera on the
- * device at the given quality level. If the device has no back-facing
- * camera, this returns null.
+ * Returns the default camcorder profile at the given quality level for the first back-facing
+ * camera on the device. If the device has no back-facing camera, this returns null.
* @param quality the target quality level for the camcorder profile
* @see #get(int, int)
*/
@@ -404,8 +456,7 @@
}
/**
- * Returns the camcorder profile for the given camera at the given
- * quality level.
+ * Returns the default camcorder profile for the given camera at the given quality level.
*
* Quality levels QUALITY_LOW, QUALITY_HIGH are guaranteed to be supported, while
* other levels may or may not be supported. The supported levels can be checked using
@@ -457,6 +508,7 @@
* @see #QUALITY_HIGH_SPEED_720P
* @see #QUALITY_HIGH_SPEED_1080P
* @see #QUALITY_HIGH_SPEED_2160P
+ * @throws IllegalArgumentException if quality is not one of the defined QUALITY_ values.
*/
public static CamcorderProfile get(int cameraId, int quality) {
if (!((quality >= QUALITY_LIST_START &&
@@ -472,7 +524,119 @@
}
/**
- * Returns true if camcorder profile exists for the first back-facing
+ * Returns all encoder profiles of a camcorder profile for the given camera at
+ * the given quality level.
+ *
+ * Quality levels QUALITY_LOW, QUALITY_HIGH are guaranteed to be supported, while
+ * other levels may or may not be supported. The supported levels can be checked using
+ * {@link #hasProfile(int, int)}.
+ * QUALITY_LOW refers to the lowest quality available, while QUALITY_HIGH refers to
+ * the highest quality available.
+ * QUALITY_LOW/QUALITY_HIGH have to match one of qcif, cif, 480p, 720p, 1080p or 2160p.
+ * E.g. if the device supports 480p, 720p, 1080p and 2160p, then low is 480p and high is
+ * 2160p.
+ *
+ * The same is true for time lapse quality levels, i.e. QUALITY_TIME_LAPSE_LOW,
+ * QUALITY_TIME_LAPSE_HIGH are guaranteed to be supported and have to match one of
+ * qcif, cif, 480p, 720p, 1080p, or 2160p.
+ *
+ * For high speed quality levels, they may or may not be supported. If a subset of the levels
+ * are supported, QUALITY_HIGH_SPEED_LOW and QUALITY_HIGH_SPEED_HIGH are guaranteed to be
+ * supported and have to match one of 480p, 720p, or 1080p.
+ *
+ * A camcorder recording session with higher quality level usually has higher output
+ * bit rate, better video and/or audio recording quality, larger video frame
+ * resolution and higher audio sampling rate, etc, than those with lower quality
+ * level.
+ *
+ * @param cameraId the id for the camera. Numeric camera ids parsed from the list received by
+ * invoking {@link CameraManager#getCameraIdList} can be used as long as they
+ * are {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE}
+ * and not
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}.
+ * @param quality the target quality level for the camcorder profile.
+ * @return null if there are no encoder profiles defined for the quality level for the
+ * given camera.
+ * @throws IllegalArgumentException if quality is not one of the defined QUALITY_ values.
+ * @see #QUALITY_LOW
+ * @see #QUALITY_HIGH
+ * @see #QUALITY_QCIF
+ * @see #QUALITY_CIF
+ * @see #QUALITY_480P
+ * @see #QUALITY_720P
+ * @see #QUALITY_1080P
+ * @see #QUALITY_2160P
+ * @see #QUALITY_TIME_LAPSE_LOW
+ * @see #QUALITY_TIME_LAPSE_HIGH
+ * @see #QUALITY_TIME_LAPSE_QCIF
+ * @see #QUALITY_TIME_LAPSE_CIF
+ * @see #QUALITY_TIME_LAPSE_480P
+ * @see #QUALITY_TIME_LAPSE_720P
+ * @see #QUALITY_TIME_LAPSE_1080P
+ * @see #QUALITY_TIME_LAPSE_2160P
+ * @see #QUALITY_HIGH_SPEED_LOW
+ * @see #QUALITY_HIGH_SPEED_HIGH
+ * @see #QUALITY_HIGH_SPEED_480P
+ * @see #QUALITY_HIGH_SPEED_720P
+ * @see #QUALITY_HIGH_SPEED_1080P
+ * @see #QUALITY_HIGH_SPEED_2160P
+ */
+ @Nullable public static EncoderProfiles getAll(
+ @NonNull String cameraId, @Quality int quality) {
+ if (!((quality >= QUALITY_LIST_START &&
+ quality <= QUALITY_LIST_END) ||
+ (quality >= QUALITY_TIME_LAPSE_LIST_START &&
+ quality <= QUALITY_TIME_LAPSE_LIST_END) ||
+ (quality >= QUALITY_HIGH_SPEED_LIST_START &&
+ quality <= QUALITY_HIGH_SPEED_LIST_END))) {
+ String errMessage = "Unsupported quality level: " + quality;
+ throw new IllegalArgumentException(errMessage);
+ }
+
+ // TODO: get all profiles
+ int id;
+ try {
+ id = Integer.valueOf(cameraId);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ CamcorderProfile cp = native_get_camcorder_profile(id, quality);
+ if (cp == null) {
+ return null;
+ };
+
+ EncoderProfiles.AudioProfile[] audioProfiles;
+ // timelapse profiles do not list audio profiles
+ if (cp.quality >= QUALITY_TIME_LAPSE_LIST_START
+ && cp.quality <= QUALITY_TIME_LAPSE_LIST_END) {
+ audioProfiles = new EncoderProfiles.AudioProfile[] { };
+ } else {
+ audioProfiles = new EncoderProfiles.AudioProfile[] {
+ new EncoderProfiles.AudioProfile(
+ cp.audioCodec,
+ cp.audioChannels,
+ cp.audioSampleRate,
+ cp.audioBitRate)
+ };
+ }
+
+ return new EncoderProfiles(
+ cp.duration,
+ cp.fileFormat,
+ new EncoderProfiles.VideoProfile[] {
+ new EncoderProfiles.VideoProfile(
+ cp.videoCodec,
+ cp.videoFrameWidth,
+ cp.videoFrameHeight,
+ cp.videoFrameRate,
+ cp.videoBitRate,
+ 0 /* TODO: get profile */)
+ },
+ audioProfiles);
+ }
+
+ /**
+ * Returns true if a camcorder profile exists for the first back-facing
* camera at the given quality level.
*
* <p>
@@ -507,7 +671,7 @@
}
/**
- * Returns true if camcorder profile exists for the given camera at
+ * Returns true if a camcorder profile exists for the given camera at
* the given quality level.
*
* <p>
diff --git a/media/java/android/media/EncoderProfiles.java b/media/java/android/media/EncoderProfiles.java
new file mode 100644
index 0000000..ca3daef
--- /dev/null
+++ b/media/java/android/media/EncoderProfiles.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import android.annotation.NonNull;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Describes a set of encoding profiles for a given media (audio and/or video) profile.
+ * These settings are read-only.
+ *
+ * <p>Currently, this is used to describe camera recording profile with more detail than {@link
+ * CamcorderProfile}, by providing encoding parameters for more than just the default audio
+ * and/or video codec.
+ *
+ * <p>The compressed output from a camera recording session contains two tracks:
+ * one for audio and one for video.
+ * <p>In the future audio-only recording profiles may be defined.
+ *
+ * <p>Each media profile specifies a set of audio and a set of video specific settings.
+ * <ul>
+ * <li> The file output format
+ * <li> Default file duration
+ * <p>Video-specific settings are:
+ * <li> Video codec format
+ * <li> Video bit rate in bits per second
+ * <li> Video frame rate in frames per second
+ * <li> Video frame width and height,
+ * <li> Video encoder profile.
+ * <p>Audio-specific settings are:
+ * <li> Audio codec format
+ * <li> Audio bit rate in bits per second,
+ * <li> Audio sample rate
+ * <li> Number of audio channels for recording.
+ * </ul>
+ */
+public class EncoderProfiles
+{
+ /**
+ * Default recording duration in seconds before the session is terminated.
+ * This is useful for applications like MMS has limited file size requirement.
+ * This could be 0 if there is no default recording duration.
+ */
+ public int getDurationSeconds() {
+ return durationSecs;
+ }
+
+ /**
+ * Recommended output file format
+ * @see android.media.MediaRecorder.OutputFormat
+ */
+ public int getFileFormat() {
+ return fileFormat;
+ }
+
+ /**
+ * Configuration for a video encoder.
+ */
+ public static class VideoProfile {
+ /**
+ * The video encoder being used for the video track
+ * @see android.media.MediaRecorder.VideoEncoder
+ */
+ public int getCodec() {
+ return codec;
+ }
+
+ /**
+ * The media type of the video encoder being used for the video track
+ * @see android.media.MediaFormat#KEY_MIME
+ */
+ public @NonNull String getMediaType() {
+ if (codec == MediaRecorder.VideoEncoder.H263) {
+ return MediaFormat.MIMETYPE_VIDEO_H263;
+ } else if (codec == MediaRecorder.VideoEncoder.H264) {
+ return MediaFormat.MIMETYPE_VIDEO_AVC;
+ } else if (codec == MediaRecorder.VideoEncoder.MPEG_4_SP) {
+ return MediaFormat.MIMETYPE_VIDEO_MPEG4;
+ } else if (codec == MediaRecorder.VideoEncoder.VP8) {
+ return MediaFormat.MIMETYPE_VIDEO_VP8;
+ } else if (codec == MediaRecorder.VideoEncoder.HEVC) {
+ return MediaFormat.MIMETYPE_VIDEO_HEVC;
+ }
+ // we should never be here
+ throw new RuntimeException("Unknown codec");
+ }
+
+ /**
+ * The target video output bitrate in bits per second
+ * <p>
+ * This is the target recorded video output bitrate if the application configures the video
+ * recording via {@link MediaRecorder#setProfile} without specifying any other
+ * {@link MediaRecorder} encoding parameters. For example, for high speed quality profiles
+ * (from {@link CamcorderProfile#QUALITY_HIGH_SPEED_LOW} to {@link
+ * CamcorderProfile#QUALITY_HIGH_SPEED_2160P}), this is the bitrate where the video is
+ * recorded with. If the application intends to record slow motion videos with the high
+ * speed quality profiles, it must set a different video bitrate that is corresponding to
+ * the desired recording output bit rate (i.e., the encoded video bitrate during normal
+ * playback) via {@link MediaRecorder#setVideoEncodingBitRate}. For example, if {@link
+ * CamcorderProfile#QUALITY_HIGH_SPEED_720P} advertises 240fps {@link #getFrameRate} and
+ * 64Mbps {@link #getBitrate} in the high speed VideoProfile, and the application
+ * intends to record 1/8 factor slow motion recording videos, the application must set 30fps
+ * via {@link MediaRecorder#setVideoFrameRate} and 8Mbps ( {@link #getBitrate} * slow motion
+ * factor) via {@link MediaRecorder#setVideoEncodingBitRate}. Failing to do so will result
+ * in videos with unexpected frame rate and bit rate, or {@link MediaRecorder} error if the
+ * output bit rate exceeds the encoder limit. If the application intends to do the video
+ * recording with {@link MediaCodec} encoder, it must set each individual field of {@link
+ * MediaFormat} similarly according to this VideoProfile.
+ * </p>
+ *
+ * @see #getFrameRate
+ * @see MediaRecorder
+ * @see MediaCodec
+ * @see MediaFormat
+ */
+ public int getBitrate() {
+ return bitrate;
+ }
+
+ /**
+ * The target video frame rate in frames per second.
+ * <p>
+ * This is the target recorded video output frame rate per second if the application
+ * configures the video recording via {@link MediaRecorder#setProfile} without specifying
+ * any other {@link MediaRecorder} encoding parameters. For example, for high speed quality
+ * profiles (from {@link CamcorderProfile#QUALITY_HIGH_SPEED_LOW} to {@link
+ * CamcorderProfile#QUALITY_HIGH_SPEED_2160P}), this is the frame rate where the video is
+ * recorded and played back with. If the application intends to create slow motion use case
+ * with the high speed quality profiles, it must set a different video frame rate that is
+ * corresponding to the desired output (playback) frame rate via {@link
+ * MediaRecorder#setVideoFrameRate}. For example, if {@link
+ * CamcorderProfile#QUALITY_HIGH_SPEED_720P} advertises 240fps {@link #getFrameRate}
+ * in the VideoProfile, and the application intends to create 1/8 factor slow motion
+ * recording videos, the application must set 30fps via {@link
+ * MediaRecorder#setVideoFrameRate}. Failing to do so will result in high speed videos with
+ * normal speed playback frame rate (240fps for above example). If the application intends
+ * to do the video recording with {@link MediaCodec} encoder, it must set each individual
+ * field of {@link MediaFormat} similarly according to this VideoProfile.
+ * </p>
+ *
+ * @see #getBitrate
+ * @see MediaRecorder
+ * @see MediaCodec
+ * @see MediaFormat
+ */
+ public int getFrameRate() {
+ return frameRate;
+ }
+
+ /**
+ * The target video frame width in pixels
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * The target video frame height in pixels
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * The video encoder profile being used for the video track.
+ * <p>
+ * This value is 0 if there is no profile defined for the video codec.
+ *
+ * @see MediaRecorder#setVideoEncodingProfileLevel
+ * @see MediaFormat#KEY_PROFILE
+ */
+ public int getProfile() {
+ return profile;
+ }
+
+ // Constructor called by JNI and CamcorderProfile
+ /* package private */ VideoProfile(int codec,
+ int width,
+ int height,
+ int frameRate,
+ int bitrate,
+ int profile) {
+ this.codec = codec;
+ this.width = width;
+ this.height = height;
+ this.frameRate = frameRate;
+ this.bitrate = bitrate;
+ this.profile = profile;
+ }
+
+ private int codec;
+ private int width;
+ private int height;
+ private int frameRate;
+ private int bitrate;
+ private int profile;
+ }
+
+ /**
+ * Returns the defined audio encoder profiles.
+ * <p>
+ * The list may be empty. This means there are no audio encoder
+ * profiles defined. Otherwise, the first profile is the default
+ * audio profile.
+ */
+ public @NonNull List<AudioProfile> getAudioProfiles() {
+ return audioProfiles;
+ }
+
+ /**
+ * Returns the defined video encoder profiles.
+ * <p>
+ * The list may be empty. This means there are no video encoder
+ * profiles defined. Otherwise, the first profile is the default
+ * video profile.
+ */
+ public @NonNull List<VideoProfile> getVideoProfiles() {
+ return videoProfiles;
+ }
+
+ /**
+ * Configuration for an audio encoder.
+ */
+ public static class AudioProfile {
+ /**
+ * The audio encoder being used for the audio track.
+ * @see android.media.MediaRecorder.AudioEncoder
+ */
+ public int getCodec() {
+ return codec;
+ }
+
+ /**
+ * The media type of the audio encoder being used for the video track
+ * @see android.media.MediaFormat#KEY_MIME
+ */
+ public @NonNull String getMediaType() {
+ if (codec == MediaRecorder.AudioEncoder.AMR_NB) {
+ return MediaFormat.MIMETYPE_AUDIO_AMR_NB;
+ } else if (codec == MediaRecorder.AudioEncoder.AMR_WB) {
+ return MediaFormat.MIMETYPE_AUDIO_AMR_WB;
+ } else if (codec == MediaRecorder.AudioEncoder.AAC
+ || codec == MediaRecorder.AudioEncoder.HE_AAC
+ || codec == MediaRecorder.AudioEncoder.AAC_ELD) {
+ return MediaFormat.MIMETYPE_AUDIO_AAC;
+ } else if (codec == MediaRecorder.AudioEncoder.VORBIS) {
+ return MediaFormat.MIMETYPE_AUDIO_VORBIS;
+ } else if (codec == MediaRecorder.AudioEncoder.OPUS) {
+ return MediaFormat.MIMETYPE_AUDIO_OPUS;
+ }
+ // we should never be here
+ throw new RuntimeException("Unknown codec");
+ }
+
+ /**
+ * The target audio output bitrate in bits per second
+ */
+ public int getBitrate() {
+ return bitrate;
+ }
+
+ /**
+ * The audio sampling rate used for the audio track
+ */
+ public int getSampleRate() {
+ return sampleRate;
+ }
+
+ /**
+ * The number of audio channels used for the audio track
+ */
+ public int getChannels() {
+ return channels;
+ }
+
+ /**
+ * The audio encoder profile being used for the audio track
+ * <p>
+ * This value is 0 if there is no profile defined for the audio codec.
+ * @see MediaFormat#KEY_PROFILE
+ */
+ public int getProfile() {
+ if (codec == MediaRecorder.AudioEncoder.AAC) {
+ return MediaCodecInfo.CodecProfileLevel.AACObjectMain;
+ } else if (codec == MediaRecorder.AudioEncoder.HE_AAC) {
+ return MediaCodecInfo.CodecProfileLevel.AACObjectHE;
+ } else if (codec == MediaRecorder.AudioEncoder.AAC_ELD) {
+ return MediaCodecInfo.CodecProfileLevel.AACObjectELD;
+ }
+ return 0;
+ }
+
+
+ // Constructor called by JNI and CamcorderProfile
+ /* package private */ AudioProfile(
+ int codec,
+ int channels,
+ int sampleRate,
+ int bitrate) {
+ this.codec = codec;
+ this.channels = channels;
+ this.sampleRate = sampleRate;
+ this.bitrate = bitrate;
+ }
+
+ private int codec;
+ private int channels;
+ private int sampleRate;
+ private int bitrate;
+ }
+
+ //static {
+ // System.loadLibrary("media_jni");
+ //native_init();
+ //}
+
+ private int durationSecs;
+ private int fileFormat;
+ // non-modifiable lists
+ private @NonNull List<AudioProfile> audioProfiles;
+ private @NonNull List<VideoProfile> videoProfiles;
+
+ // Constructor called by JNI and CamcorderProfile
+ /* package private */ EncoderProfiles(
+ int duration,
+ int fileFormat,
+ VideoProfile[] videoProfiles,
+ AudioProfile[] audioProfiles) {
+ this.durationSecs = duration;
+ this.fileFormat = fileFormat;
+ this.videoProfiles = Collections.unmodifiableList(Arrays.asList(videoProfiles));
+ this.audioProfiles = Collections.unmodifiableList(Arrays.asList(audioProfiles));
+ }
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ee945d5..36cf989 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -374,4 +374,13 @@
long getAdditionalOutputDeviceDelay(in AudioDeviceAttributes device);
long getMaxAdditionalOutputDeviceDelay(in AudioDeviceAttributes device);
+
+ int requestAudioFocusForTest(in AudioAttributes aa, int durationHint, IBinder cb,
+ in IAudioFocusDispatcher fd, in String clientId, in String callingPackageName,
+ int uid, int sdk);
+
+ int abandonAudioFocusForTest(in IAudioFocusDispatcher fd, in String clientId,
+ in AudioAttributes aa, in String callingPackageName);
+
+ long getFadeOutDurationOnFocusLossMillis(in AudioAttributes aa);
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 38df92b..b51777c 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -4712,7 +4712,7 @@
/**
* Contains description of a parameter.
*/
- public class ParameterDescriptor {
+ public static class ParameterDescriptor {
private ParameterDescriptor() {}
/**
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 06d0eb0..c3b1bca 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -571,6 +571,14 @@
public static final String FEATURE_LowLatency = "low-latency";
/**
+ * <b>video encoder only</b>: codec supports quantization parameter bounds.
+ * @see MediaFormat#KEY_VIDEO_QP_MAX
+ * @see MediaFormat#KEY_VIDEO_QP_MIN
+ */
+ @SuppressLint("AllUpper")
+ public static final String FEATURE_QpBounds = "qp-bounds";
+
+ /**
* Query codec feature capabilities.
* <p>
* These features are supported to be used by the codec. These
@@ -606,6 +614,7 @@
new Feature(FEATURE_IntraRefresh, (1 << 0), false),
new Feature(FEATURE_MultipleFrames, (1 << 1), false),
new Feature(FEATURE_DynamicTimestamp, (1 << 2), false),
+ new Feature(FEATURE_QpBounds, (1 << 3), false),
};
/** @hide */
@@ -3434,11 +3443,14 @@
public static final int BITRATE_MODE_VBR = 1;
/** Constant bitrate mode */
public static final int BITRATE_MODE_CBR = 2;
+ /** Constant bitrate mode with frame drops */
+ public static final int BITRATE_MODE_CBR_FD = 3;
private static final Feature[] bitrates = new Feature[] {
new Feature("VBR", BITRATE_MODE_VBR, true),
new Feature("CBR", BITRATE_MODE_CBR, false),
- new Feature("CQ", BITRATE_MODE_CQ, false)
+ new Feature("CQ", BITRATE_MODE_CQ, false),
+ new Feature("CBR-FD", BITRATE_MODE_CBR_FD, false)
};
private static int parseBitrateMode(String mode) {
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 4e8a273..af0a9cf 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -985,19 +985,27 @@
/**
* A key describing the maximum Quantization Parameter allowed for encoding video.
- * This key applies to all three video frame types (I, P, and B). This value fills
- * in for any of the frame-specific #KEY_VIDEO_QP_I_MAX, #KEY_VIDEO_QP_P_MAX, or
- * #KEY_VIDEO_QP_B_MAX keys that are not specified
+ * This key applies to all three video picture types (I, P, and B).
+ * The value is used directly for picture type I; a per-mime formula is used
+ * to calculate the value for the remaining picture types.
+ *
+ * This calculation can be avoided by directly specifying values for each picture type
+ * using the type-specific keys {@link #KEY_VIDEO_QP_I_MAX}, {@link #KEY_VIDEO_QP_P_MAX},
+ * and {@link #KEY_VIDEO_QP_B_MAX}.
*
* The associated value is an integer.
*/
public static final String KEY_VIDEO_QP_MAX = "video-qp-max";
/**
- * A key describing the maximum Quantization Parameter allowed for encoding video.
- * This key applies to all three video frame types (I, P, and B). This value fills
- * in for any of the frame-specific #KEY_VIDEO_QP_I_MIN, #KEY_VIDEO_QP_P_MIN, or
- * #KEY_VIDEO_QP_B_MIN keys that are not specified
+ * A key describing the minimum Quantization Parameter allowed for encoding video.
+ * This key applies to all three video frame types (I, P, and B).
+ * The value is used directly for picture type I; a per-mime formula is used
+ * to calculate the value for the remaining picture types.
+ *
+ * This calculation can be avoided by directly specifying values for each picture type
+ * using the type-specific keys {@link #KEY_VIDEO_QP_I_MIN}, {@link #KEY_VIDEO_QP_P_MIN},
+ * and {@link #KEY_VIDEO_QP_B_MIN}.
*
* The associated value is an integer.
*/
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index f960ff2..5eb57da 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -753,6 +753,47 @@
}
/**
+ * Uses the settings from an AudioProfile for recording.
+ * <p>
+ * This method should be called after the video AND audio sources are set, and before
+ * setOutputFile().
+ * <p>
+ * This method can be used instead of {@link #setProfile} when using EncoderProfiles.
+ *
+ * @param profile the AudioProfile to use
+ * @see android.media.EncoderProfiles
+ * @see android.media.CamcorderProfile#getAll
+ */
+ public void setAudioProfile(@NonNull EncoderProfiles.AudioProfile profile) {
+ setAudioEncodingBitRate(profile.getBitrate());
+ setAudioChannels(profile.getChannels());
+ setAudioSamplingRate(profile.getSampleRate());
+ setAudioEncoder(profile.getCodec());
+ }
+
+ /**
+ * Uses the settings from a VideoProfile object for recording.
+ * <p>
+ * This method should be called after the video AND audio sources are set, and before
+ * setOutputFile().
+ * <p>
+ * This method can be used instead of {@link #setProfile} when using EncoderProfiles.
+ *
+ * @param profile the VideoProfile to use
+ * @see android.media.EncoderProfiles
+ * @see android.media.CamcorderProfile#getAll
+ */
+ public void setVideoProfile(@NonNull EncoderProfiles.VideoProfile profile) {
+ setVideoFrameRate(profile.getFrameRate());
+ setVideoSize(profile.getWidth(), profile.getHeight());
+ setVideoEncodingBitRate(profile.getBitrate());
+ setVideoEncoder(profile.getCodec());
+ if (profile.getProfile() > 0) {
+ setVideoEncodingProfileLevel(profile.getProfile(), 0 /* level */);
+ }
+ }
+
+ /**
* Set video frame capture rate. This can be used to set a different video frame capture
* rate than the recorded video's playback rate. This method also sets the recording mode
* to time lapse. In time lapse video recording, only video is recorded. Audio related
@@ -1961,4 +2002,3 @@
}
}
-
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 8daa303..1f6855a 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -194,6 +194,14 @@
/**
* Starts scanning remote routes.
+ * <p>
+ * Route discovery can happen even when the {@link #startScan()} is not called.
+ * This is because the scanning could be started before by other apps.
+ * Therefore, calling this method after calling {@link #stopScan()} does not necessarily mean
+ * that the routes found before are removed and added again.
+ * <p>
+ * Use {@link RouteCallback} to get the route related events.
+ * <p>
* Note that calling start/stopScan is applied to all system routers in the same process.
*
* @see #stopScan()
@@ -208,6 +216,15 @@
/**
* Stops scanning remote routes to reduce resource consumption.
+ * <p>
+ * Route discovery can be continued even after this method is called.
+ * This is because the scanning is only turned off when all the apps stop scanning.
+ * Therefore, calling this method does not necessarily mean the routes are removed.
+ * Also, for the same reason it does not mean that {@link RouteCallback#onRoutesAdded(List)}
+ * is not called afterwards.
+ * <p>
+ * Use {@link RouteCallback} to get the route related events.
+ * <p>
* Note that calling start/stopScan is applied to all system routers in the same process.
*
* @see #startScan()
@@ -299,24 +316,6 @@
}
/**
- * Registers a callback to receive route related events when they change.
- * <p>
- * If the specified callback is already registered, its registration will be updated for the
- * given {@link Executor executor}.
- * <p>
- * This will be no-op for non-system routers.
- * @hide
- */
- @SystemApi
- public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull RouteCallback routeCallback) {
- if (!isSystemRouter()) {
- return;
- }
- registerRouteCallback(executor, routeCallback, RouteDiscoveryPreference.EMPTY);
- }
-
- /**
* Registers a callback to discover routes and to receive events when they change.
* <p>
* If the specified callback is already registered, its registration will be updated for the
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 6fefbe1..758a813 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -148,6 +148,14 @@
/**
* Starts scanning remote routes.
+ * <p>
+ * Route discovery can happen even when the {@link #startScan()} is not called.
+ * This is because the scanning could be started before by other apps.
+ * Therefore, calling this method after calling {@link #stopScan()} does not necessarily mean
+ * that the routes found before are removed and added again.
+ * <p>
+ * Use {@link Callback} to get the route related events.
+ * <p>
* @see #stopScan()
*/
public void startScan() {
@@ -163,6 +171,15 @@
/**
* Stops scanning remote routes to reduce resource consumption.
+ * <p>
+ * Route discovery can be continued even after this method is called.
+ * This is because the scanning is only turned off when all the apps stop scanning.
+ * Therefore, calling this method does not necessarily mean the routes are removed.
+ * Also, for the same reason it does not mean that {@link Callback#onRoutesAdded(List)}
+ * is not called afterwards.
+ * <p>
+ * Use {@link Callback} to get the route related events.
+ *
* @see #startScan()
*/
public void stopScan() {
diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index 2f95247..37fee84 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -68,9 +69,10 @@
private final Bundle mExtras;
/**
- * An empty discovery preference
+ * An empty discovery preference.
* @hide
*/
+ @SystemApi
public static final RouteDiscoveryPreference EMPTY =
new Builder(Collections.emptyList(), false).build();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index de353bf..a0d4492 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -24,8 +24,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.AttributionSource;
import android.content.ContentProviderClient;
import android.content.ContentValues;
+import android.content.Context;
import android.content.IContentProvider;
import android.media.MediaInserter;
import android.net.Uri;
@@ -46,7 +48,7 @@
private MediaInserter mMediaInserter;
private static final int TEST_BUFFER_SIZE = 10;
private @Mock IContentProvider mMockProvider;
- private String mPackageName;
+ private AttributionSource mAttributionSource;
private int mFilesCounter;
private int mAudioCounter;
@@ -86,11 +88,14 @@
super.setUp();
MockitoAnnotations.initMocks(this);
- final ContentProviderClient client = new ContentProviderClient(
- getInstrumentation().getContext().createFeatureContext(TEST_FEATURE_ID)
+ final Context attributionContext = getInstrumentation().getContext()
+ .createFeatureContext(TEST_FEATURE_ID);
+
+ final ContentProviderClient client = new ContentProviderClient(attributionContext
.getContentResolver(), mMockProvider, true);
+
mMediaInserter = new MediaInserter(client, TEST_BUFFER_SIZE);
- mPackageName = getInstrumentation().getContext().getPackageName();
+ mAttributionSource = attributionContext.getAttributionSource();
mFilesCounter = 0;
mAudioCounter = 0;
mVideoCounter = 0;
@@ -144,13 +149,13 @@
fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
- verify(mMockProvider, never()).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ verify(mMockProvider, never()).bulkInsert(eq(mAttributionSource), any(),
any());
}
@SmallTest
public void testInsertContentsEqualToBufferSize() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), any(),
any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE);
@@ -158,13 +163,13 @@
fillBuffer(sVideoUri, TEST_BUFFER_SIZE);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE);
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ verify(mMockProvider, times(4)).bulkInsert(eq(mAttributionSource), any(),
any());
}
@SmallTest
public void testInsertContentsMoreThanBufferSize() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), any(),
any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE + 1);
@@ -172,7 +177,7 @@
fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ verify(mMockProvider, times(4)).bulkInsert(eq(mAttributionSource), any(),
any());
}
@@ -183,7 +188,7 @@
@SmallTest
public void testFlushAllWithSomeContents() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), any(),
any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
@@ -192,13 +197,13 @@
fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
mMediaInserter.flushAll();
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ verify(mMockProvider, times(4)).bulkInsert(eq(mAttributionSource), any(),
any());
}
@SmallTest
public void testInsertContentsAfterFlushAll() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), any(),
any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
@@ -212,19 +217,19 @@
fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);
- verify(mMockProvider, times(8)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ verify(mMockProvider, times(8)).bulkInsert(eq(mAttributionSource), any(),
any());
}
@SmallTest
public void testInsertContentsWithDifferentSizePerContentType() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sFilesUri),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), eqUri(sFilesUri),
any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sAudioUri),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), eqUri(sAudioUri),
any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sVideoUri),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), eqUri(sVideoUri),
any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sImagesUri),
+ when(mMockProvider.bulkInsert(eq(mAttributionSource), eqUri(sImagesUri),
any())).thenReturn(1);
for (int i = 0; i < TEST_BUFFER_SIZE; ++i) {
@@ -234,13 +239,13 @@
fillBuffer(sImagesUri, 4);
}
- verify(mMockProvider, times(1)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ verify(mMockProvider, times(1)).bulkInsert(eq(mAttributionSource),
eqUri(sFilesUri), any());
- verify(mMockProvider, times(2)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ verify(mMockProvider, times(2)).bulkInsert(eq(mAttributionSource),
eqUri(sAudioUri), any());
- verify(mMockProvider, times(3)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ verify(mMockProvider, times(3)).bulkInsert(eq(mAttributionSource),
eqUri(sVideoUri), any());
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ verify(mMockProvider, times(4)).bulkInsert(eq(mAttributionSource),
eqUri(sImagesUri), any());
}
}
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index c00da76..522e644 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedshåndtering"</string>
+ <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedsadministrator"</string>
<string name="chooser_title" msgid="2262294130493605839">"Vælg den enhed (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), som skal administreres af <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 5133bd2..2caa3a9 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -20,7 +20,7 @@
<string name="chooser_title" msgid="2262294130493605839">"Gerät (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) auswählen, das von <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> verwaltet werden soll"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
<string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Zulassen, dass <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dein Gerät <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> verwalten kann"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Zulassen, dass <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dein Gerät <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> verwalten darf"</string>
<string name="profile_summary" msgid="2059360676631420073">"Diese App wird zur Verwaltung des Profils „<xliff:g id="PROFILE_NAME">%1$s</xliff:g>“ benötigt. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
<string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string>
<string name="consent_no" msgid="2640796915611404382">"Nicht zulassen"</string>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 226efed..764505e 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -20,7 +20,7 @@
<string name="chooser_title" msgid="2262294130493605839">"Aukeratu <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
<string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Eman baimena <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari <strong><<xliff:g id="DEVICE_NAME">%2$s</xliff:g>strong>) kudea dezan"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Eman <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> kudeatzeko baimena <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari"</string>
<string name="profile_summary" msgid="2059360676631420073">"Aplikazioa <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kudeatzeko beharrezkoa da. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
<string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string>
<string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index cecddf2..ca86d76 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -20,8 +20,7 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
<string name="profile_name_watch" msgid="576290739483672360">"पाहा"</string>
- <!-- no translation found for confirmation_title (8455544820286920304) -->
- <skip />
+ <string name="confirmation_title" msgid="8455544820286920304">"तुमचे <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> व्यवस्थापित करण्यासाठी <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला अनुमती द्या"</string>
<string name="profile_summary" msgid="2059360676631420073">"तुमची <xliff:g id="PROFILE_NAME">%1$s</xliff:g> प्रोफाइल व्यवस्थापित करण्यासाठी हे ॲप आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
<string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string>
<string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index bf1aca8..54f4f8c 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -20,8 +20,7 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
- <!-- no translation found for confirmation_title (8455544820286920304) -->
- <skip />
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
<string name="profile_summary" msgid="2059360676631420073">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਇਹ ਐਪ ਲੋੜੀਂਦੀ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
<string name="consent_yes" msgid="8344487259618762872">"ਇਜਾਜ਼ਤ ਦਿਓ"</string>
<string name="consent_no" msgid="2640796915611404382">"ਇਜਾਜ਼ਤ ਨਾ ਦਿਓ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index be128cf..c855cf2 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -20,8 +20,7 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ద్వారా మేనేజ్ చేయబడటానికి ఒక <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
<string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
- <!-- no translation found for confirmation_title (8455544820286920304) -->
- <skip />
+ <string name="confirmation_title" msgid="8455544820286920304">"మీ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ను మేనేజ్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను అనుమతించండి;"</string>
<string name="profile_summary" msgid="2059360676631420073">"మీ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను మేనేజ్ చేయడానికి ఈ యాప్ అవసరం. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
<string name="consent_yes" msgid="8344487259618762872">"అనుమతించు"</string>
<string name="consent_no" msgid="2640796915611404382">"అనుమతించవద్దు"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index 15e81a9..2e81a17 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string>
+ <string name="app_label" msgid="4470785958457506021">"隨附裝置管理工具"</string>
<string name="chooser_title" msgid="2262294130493605839">"選擇由 <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index 80c68f2..74cecdd 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -35,7 +35,6 @@
":framework-javastream-protos",
],
apex_available: [
- "//apex_available:platform",
"com.android.tethering",
],
jarjar_rules: "jarjar-rules-proto.txt",
@@ -82,11 +81,13 @@
java_sdk_library {
name: "framework-connectivity",
- api_only: true,
+ sdk_version: "module_current",
+ min_sdk_version: "30",
defaults: ["framework-module-defaults"],
installable: true,
srcs: [
":framework-connectivity-sources",
+ ":net-utils-framework-common-srcs",
],
aidl: {
include_dirs: [
@@ -98,10 +99,33 @@
"frameworks/native/aidl/binder", // For PersistableBundle.aidl
],
},
+ impl_only_libs: [
+ // TODO (b/183097033) remove once module_current includes core_platform
+ "stable.core.platform.api.stubs",
+ "framework-tethering.stubs.module_lib",
+ "framework-wifi.stubs.module_lib",
+ "net-utils-device-common",
+ ],
libs: [
"unsupportedappusage",
],
+ static_libs: [
+ "framework-connectivity-protos",
+ ],
+ jarjar_rules: "jarjar-rules.txt",
permitted_packages: ["android.net"],
+ impl_library_visibility: [
+ "//packages/modules/Connectivity/Tethering/apex",
+ // In preparation for future move
+ "//packages/modules/Connectivity/apex",
+ "//packages/modules/Connectivity/service",
+ "//frameworks/base/packages/Connectivity/service",
+ "//frameworks/base",
+ "//packages/modules/Connectivity/Tethering/tests/unit",
+ ],
+ apex_available: [
+ "com.android.tethering",
+ ],
}
cc_defaults {
@@ -149,37 +173,6 @@
shared_libs: ["libandroid"],
stl: "libc++_static",
apex_available: [
- "//apex_available:platform",
"com.android.tethering",
],
}
-
-java_library {
- name: "framework-connectivity.impl",
- sdk_version: "module_current",
- min_sdk_version: "30",
- srcs: [
- ":framework-connectivity-sources",
- ],
- aidl: {
- include_dirs: [
- "frameworks/base/core/java", // For framework parcelables
- "frameworks/native/aidl/binder", // For PersistableBundle.aidl
- ],
- },
- libs: [
- // TODO (b/183097033) remove once module_current includes core_current
- "stable.core.platform.api.stubs",
- "framework-tethering",
- "framework-wifi",
- "unsupportedappusage",
- ],
- static_libs: [
- "framework-connectivity-protos",
- "net-utils-device-common",
- ],
- jarjar_rules: "jarjar-rules.txt",
- apex_available: ["com.android.tethering"],
- installable: true,
- permitted_packages: ["android.net"],
-}
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 0a9560a..7428c6e 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -311,6 +311,7 @@
field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d
field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
+ field public static final int NET_CAPABILITY_HEAD_UNIT = 32; // 0x20
field public static final int NET_CAPABILITY_IA = 7; // 0x7
field public static final int NET_CAPABILITY_IMS = 4; // 0x4
field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
@@ -334,6 +335,7 @@
field public static final int TRANSPORT_CELLULAR = 0; // 0x0
field public static final int TRANSPORT_ETHERNET = 3; // 0x3
field public static final int TRANSPORT_LOWPAN = 6; // 0x6
+ field public static final int TRANSPORT_USB = 8; // 0x8
field public static final int TRANSPORT_VPN = 4; // 0x4
field public static final int TRANSPORT_WIFI = 1; // 0x1
field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 2bf807c4..2065559 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -20,6 +20,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAvoidUnvalidated(@NonNull android.net.Network);
method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setGlobalProxy(@Nullable android.net.ProxyInfo);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setLegacyLockdownVpnEnabled(boolean);
+ method public static void setPrivateDnsMode(@NonNull android.content.Context, @NonNull String);
method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setRequireVpnForUids(boolean, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
@@ -30,10 +31,12 @@
field public static final String ACTION_PROMPT_UNVALIDATED = "android.net.action.PROMPT_UNVALIDATED";
field public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 262144; // 0x40000
field public static final int BLOCKED_METERED_REASON_DATA_SAVER = 65536; // 0x10000
+ field public static final int BLOCKED_METERED_REASON_MASK = -65536; // 0xffff0000
field public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 131072; // 0x20000
field public static final int BLOCKED_REASON_APP_STANDBY = 4; // 0x4
field public static final int BLOCKED_REASON_BATTERY_SAVER = 1; // 0x1
field public static final int BLOCKED_REASON_DOZE = 2; // 0x2
+ field public static final int BLOCKED_REASON_LOCKDOWN_VPN = 16; // 0x10
field public static final int BLOCKED_REASON_NONE = 0; // 0x0
field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8
field public static final String PRIVATE_DNS_MODE_OFF = "off";
@@ -43,6 +46,56 @@
field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
}
+ public static class ConnectivityManager.NetworkCallback {
+ method public void onBlockedStatusChanged(@NonNull android.net.Network, int);
+ }
+
+ public class ConnectivitySettingsManager {
+ method public static void clearGlobalProxy(@NonNull android.content.Context);
+ method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context);
+ method public static int getCaptivePortalMode(@NonNull android.content.Context, int);
+ method @NonNull public static java.time.Duration getConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method @NonNull public static android.util.Range<java.lang.Integer> getDnsResolverSampleRanges(@NonNull android.content.Context);
+ method @NonNull public static java.time.Duration getDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static int getDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, int);
+ method @Nullable public static android.net.ProxyInfo getGlobalProxy(@NonNull android.content.Context);
+ method @NonNull public static java.time.Duration getMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static boolean getMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
+ method @Nullable public static String getMobileDataPreferredApps(@NonNull android.content.Context);
+ method public static int getNetworkAvoidBadWifi(@NonNull android.content.Context);
+ method @Nullable public static String getNetworkMeteredMultipathPreference(@NonNull android.content.Context);
+ method public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, int);
+ method @NonNull public static java.time.Duration getNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method @NonNull public static String getPrivateDnsDefaultMode(@NonNull android.content.Context);
+ method @Nullable public static String getPrivateDnsHostname(@NonNull android.content.Context);
+ method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean);
+ method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String);
+ method public static void setCaptivePortalMode(@NonNull android.content.Context, int);
+ method public static void setConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setDnsResolverSampleRanges(@NonNull android.content.Context, @NonNull android.util.Range<java.lang.Integer>);
+ method public static void setDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, @IntRange(from=0, to=100) int);
+ method public static void setGlobalProxy(@NonNull android.content.Context, @NonNull android.net.ProxyInfo);
+ method public static void setMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setMobileDataAlwaysOn(@NonNull android.content.Context, boolean);
+ method public static void setMobileDataPreferredApps(@NonNull android.content.Context, @Nullable String);
+ method public static void setNetworkAvoidBadWifi(@NonNull android.content.Context, int);
+ method public static void setNetworkMeteredMultipathPreference(@NonNull android.content.Context, @NonNull String);
+ method public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, @IntRange(from=0) int);
+ method public static void setNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration);
+ method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull String);
+ method public static void setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String);
+ method public static void setWifiAlwaysRequested(@NonNull android.content.Context, boolean);
+ method public static void setWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration);
+ field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2
+ field public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; // 0x0
+ field public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; // 0x1
+ field public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2; // 0x2
+ field public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0; // 0x0
+ field public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1; // 0x1
+ }
+
public final class NetworkAgentConfig implements android.os.Parcelable {
method @Nullable public String getSubscriberId();
method public boolean isBypassableVpn();
@@ -126,10 +179,12 @@
}
public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
- ctor public VpnTransportInfo(int);
+ ctor public VpnTransportInfo(int, @Nullable String);
method public int describeContents();
+ method @NonNull public android.net.VpnTransportInfo makeCopy(long);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
+ field @Nullable public final String sessionId;
field public final int type;
}
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index f3f5533..6c3b620 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -218,6 +218,8 @@
method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
method public void onAutomaticReconnectDisabled();
method public void onBandwidthUpdateRequested();
+ method public void onNetworkCreated();
+ method public void onNetworkDestroyed();
method public void onNetworkUnwanted();
method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
method public void onQosCallbackUnregistered(int);
@@ -236,6 +238,7 @@
method public final void sendQosSessionLost(int, int, int);
method public final void sendSocketKeepaliveEvent(int, int);
method @Deprecated public void setLegacySubtype(int, @NonNull String);
+ method public void setTeardownDelayMs(@IntRange(from=0, to=0x1388) int);
method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method public void unregister();
field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
@@ -275,11 +278,13 @@
method @NonNull public int[] getTransportTypes();
method public boolean isPrivateDnsBroken();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+ field public static final int NET_CAPABILITY_BIP = 31; // 0x1f
field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b
+ field public static final int NET_CAPABILITY_VSIM = 30; // 0x1e
}
public static final class NetworkCapabilities.Builder {
@@ -310,9 +315,16 @@
method public int getProviderId();
method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest);
method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void registerNetworkOffer(@NonNull android.net.NetworkScore, @NonNull android.net.NetworkCapabilities, @NonNull java.util.concurrent.Executor, @NonNull android.net.NetworkProvider.NetworkOfferCallback);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkOffer(@NonNull android.net.NetworkProvider.NetworkOfferCallback);
field public static final int ID_NONE = -1; // 0xffffffff
}
+ public static interface NetworkProvider.NetworkOfferCallback {
+ method public void onNetworkNeeded(@NonNull android.net.NetworkRequest);
+ method public void onNetworkUnneeded(@NonNull android.net.NetworkRequest);
+ }
+
public class NetworkReleasedException extends java.lang.Exception {
}
@@ -327,14 +339,18 @@
public final class NetworkScore implements android.os.Parcelable {
method public int describeContents();
+ method public int getKeepConnectedReason();
method public int getLegacyInt();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkScore> CREATOR;
+ field public static final int KEEP_CONNECTED_FOR_HANDOVER = 1; // 0x1
+ field public static final int KEEP_CONNECTED_NONE = 0; // 0x0
}
public static final class NetworkScore.Builder {
ctor public NetworkScore.Builder();
method @NonNull public android.net.NetworkScore build();
+ method @NonNull public android.net.NetworkScore.Builder setKeepConnectedReason(int);
method @NonNull public android.net.NetworkScore.Builder setLegacyInt(int);
}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index a73d76e..350b47f 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -38,7 +38,9 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -875,6 +877,17 @@
public static final int BLOCKED_REASON_RESTRICTED_MODE = 1 << 3;
/**
+ * Flag to indicate that an app is blocked because it is subject to an always-on VPN but the VPN
+ * is not currently connected.
+ *
+ * @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean)
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int BLOCKED_REASON_LOCKDOWN_VPN = 1 << 4;
+
+ /**
* Flag to indicate that an app is subject to Data saver restrictions that would
* result in its metered network access being blocked.
*
@@ -911,12 +924,21 @@
BLOCKED_REASON_DOZE,
BLOCKED_REASON_APP_STANDBY,
BLOCKED_REASON_RESTRICTED_MODE,
+ BLOCKED_REASON_LOCKDOWN_VPN,
BLOCKED_METERED_REASON_DATA_SAVER,
BLOCKED_METERED_REASON_USER_RESTRICTED,
BLOCKED_METERED_REASON_ADMIN_DISABLED,
})
public @interface BlockedReason {}
+ /**
+ * Set of blocked reasons that are only applicable on metered networks.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int BLOCKED_METERED_REASON_MASK = 0xffff0000;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private final IConnectivityManager mService;
@@ -3326,9 +3348,10 @@
* Register or update a network offer with ConnectivityService.
*
* ConnectivityService keeps track of offers made by the various providers and matches
- * them to networking requests made by apps or the system. The provider supplies a score
- * and the capabilities of the network it might be able to bring up ; these act as filters
- * used by ConnectivityService to only send those requests that can be fulfilled by the
+ * them to networking requests made by apps or the system. A callback identifies an offer
+ * uniquely, and later calls with the same callback update the offer. The provider supplies a
+ * score and the capabilities of the network it might be able to bring up ; these act as
+ * filters used by ConnectivityService to only send those requests that can be fulfilled by the
* provider.
*
* The provider is under no obligation to be able to bring up the network it offers at any
@@ -3344,7 +3367,7 @@
* @param score The prospective score of the network.
* @param caps The prospective capabilities of the network.
* @param callback The callback to call when this offer is needed or unneeded.
- * @hide
+ * @hide exposed via the NetworkProvider class.
*/
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
@@ -3367,7 +3390,7 @@
*
* @param callback The callback passed at registration time. This must be the same object
* that was passed to {@link #offerNetwork}
- * @hide
+ * @hide exposed via the NetworkProvider class.
*/
public void unofferNetwork(@NonNull final INetworkOfferCallback callback) {
try {
@@ -3497,12 +3520,30 @@
* @param blocked Whether access to the {@link Network} is blocked due to system policy.
* @hide
*/
- public void onAvailable(@NonNull Network network,
+ public final void onAvailable(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
- @NonNull LinkProperties linkProperties, boolean blocked) {
+ @NonNull LinkProperties linkProperties, @BlockedReason int blocked) {
// Internally only this method is called when a new network is available, and
// it calls the callback in the same way and order that older versions used
// to call so as not to change the behavior.
+ onAvailable(network, networkCapabilities, linkProperties, blocked != 0);
+ onBlockedStatusChanged(network, blocked);
+ }
+
+ /**
+ * Legacy variant of onAvailable that takes a boolean blocked reason.
+ *
+ * This method has never been public API, but it's not final, so there may be apps that
+ * implemented it and rely on it being called. Do our best not to break them.
+ * Note: such apps will also get a second call to onBlockedStatusChanged immediately after
+ * this method is called. There does not seem to be a way to avoid this.
+ * TODO: add a compat check to move apps off this method, and eventually stop calling it.
+ *
+ * @hide
+ */
+ public void onAvailable(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties, boolean blocked) {
onAvailable(network);
if (!networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
@@ -3510,7 +3551,7 @@
}
onCapabilitiesChanged(network, networkCapabilities);
onLinkPropertiesChanged(network, linkProperties);
- onBlockedStatusChanged(network, blocked);
+ // No call to onBlockedStatusChanged here. That is done by the caller.
}
/**
@@ -3674,6 +3715,27 @@
*/
public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
+ /**
+ * Called when access to the specified network is blocked or unblocked, or the reason for
+ * access being blocked changes.
+ *
+ * If a NetworkCallback object implements this method,
+ * {@link #onBlockedStatusChanged(Network, boolean)} will not be called.
+ *
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions : calling these methods while in a
+ * callback may return an outdated or even a null object.
+ *
+ * @param network The {@link Network} whose blocked status has changed.
+ * @param blocked The blocked status of this {@link Network}.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public void onBlockedStatusChanged(@NonNull Network network, @BlockedReason int blocked) {
+ onBlockedStatusChanged(network, blocked != 0);
+ }
+
private NetworkRequest networkRequest;
private final int mFlags;
}
@@ -3788,7 +3850,7 @@
case CALLBACK_AVAILABLE: {
NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
LinkProperties lp = getObject(message, LinkProperties.class);
- callback.onAvailable(network, cap, lp, message.arg1 != 0);
+ callback.onAvailable(network, cap, lp, message.arg1);
break;
}
case CALLBACK_LOSING: {
@@ -3822,8 +3884,7 @@
break;
}
case CALLBACK_BLK_CHANGED: {
- boolean blocked = message.arg1 != 0;
- callback.onBlockedStatusChanged(network, blocked);
+ callback.onBlockedStatusChanged(network, message.arg1);
}
}
}
@@ -5426,4 +5487,23 @@
if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
return mode;
}
+
+ /**
+ * Set private DNS mode to settings.
+ *
+ * @param context The {@link Context} to set the private DNS mode.
+ * @param mode The private dns mode. This should be one of the PRIVATE_DNS_MODE_* constants.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static void setPrivateDnsMode(@NonNull Context context,
+ @NonNull @PrivateDnsMode String mode) {
+ if (!(mode == PRIVATE_DNS_MODE_OFF
+ || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC
+ || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
+ throw new IllegalArgumentException("Invalid private dns mode");
+ }
+ Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_MODE, mode);
+ }
}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
index bbd8393..9a00055 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java
@@ -16,16 +16,38 @@
package android.net;
+import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER;
+import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE;
+import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+
import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.ConnectivityManager.MultipathPreference;
+import android.net.ConnectivityManager.PrivateDnsMode;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Range;
+
+import com.android.net.module.util.ProxyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
+import java.util.List;
/**
* A manager class for connectivity module settings.
*
* @hide
*/
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public class ConnectivitySettingsManager {
private ConnectivitySettingsManager() {}
@@ -45,12 +67,16 @@
* Network activity refers to transmitting or receiving data on the network interfaces.
*
* Tracking is disabled if set to zero or negative value.
+ *
+ * @hide
*/
public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile";
/**
* Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
* but for Wifi network.
+ *
+ * @hide
*/
public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
@@ -58,12 +84,16 @@
/**
* Sample validity in seconds to configure for the system DNS resolver.
+ *
+ * @hide
*/
public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS =
"dns_resolver_sample_validity_seconds";
/**
* Success threshold in percent for use with the system DNS resolver.
+ *
+ * @hide
*/
public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT =
"dns_resolver_success_threshold_percent";
@@ -71,24 +101,35 @@
/**
* Minimum number of samples needed for statistics to be considered meaningful in the
* system DNS resolver.
+ *
+ * @hide
*/
public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples";
/**
* Maximum number taken into account for statistics purposes in the system DNS resolver.
+ *
+ * @hide
*/
public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples";
+ private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
+ private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
+
/** Network switch notification settings */
/**
* The maximum number of notifications shown in 24 hours when switching networks.
+ *
+ * @hide
*/
public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT =
"network_switch_notification_daily_limit";
/**
* The minimum time in milliseconds between notifications when switching networks.
+ *
+ * @hide
*/
public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS =
"network_switch_notification_rate_limit_millis";
@@ -98,14 +139,18 @@
/**
* The URL used for HTTP captive portal detection upon a new connection.
* A 204 response code from the server is used for validation.
+ *
+ * @hide
*/
public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
/**
* What to do when connecting a network that presents a captive portal.
- * Must be one of the CAPTIVE_PORTAL_MODE_* constants above.
+ * Must be one of the CAPTIVE_PORTAL_MODE_* constants below.
*
* The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
+ *
+ * @hide
*/
public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
@@ -139,11 +184,15 @@
/**
* Host name for global http proxy. Set via ConnectivityManager.
+ *
+ * @hide
*/
public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";
/**
* Integer host port for global http proxy. Set via ConnectivityManager.
+ *
+ * @hide
*/
public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
@@ -153,12 +202,16 @@
* Domains should be listed in a comma- separated list. Example of
* acceptable formats: ".domain1.com,my.domain2.com" Use
* ConnectivityManager to set/get.
+ *
+ * @hide
*/
public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST =
"global_http_proxy_exclusion_list";
/**
* The location PAC File for the proxy.
+ *
+ * @hide
*/
public static final String GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url";
@@ -171,11 +224,15 @@
* a specific provider. It may be used to store the provider name even when the
* mode changes so that temporarily disabling and re-enabling the specific
* provider mode does not necessitate retyping the provider hostname.
+ *
+ * @hide
*/
public static final String PRIVATE_DNS_MODE = "private_dns_mode";
/**
* The specific Private DNS provider name.
+ *
+ * @hide
*/
public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier";
@@ -185,6 +242,8 @@
* all of which require explicit user action to enable/configure. See also b/79719289.
*
* Value is a string, suitable for assignment to PRIVATE_DNS_MODE above.
+ *
+ * @hide
*/
public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode";
@@ -194,6 +253,8 @@
* The number of milliseconds to hold on to a PendingIntent based request. This delay gives
* the receivers of the PendingIntent an opportunity to make a new network request before
* the Network satisfying the request is potentially removed.
+ *
+ * @hide
*/
public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS =
"connectivity_release_pending_intent_delay_ms";
@@ -205,6 +266,8 @@
* See ConnectivityService for more info.
*
* (0 = disabled, 1 = enabled)
+ *
+ * @hide
*/
public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
@@ -217,6 +280,8 @@
* See ConnectivityService for more info.
*
* (0 = disabled, 1 = enabled)
+ *
+ * @hide
*/
public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested";
@@ -228,14 +293,637 @@
* 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
* null: Ask the user whether to switch away from bad wifi.
* 1: Avoid bad wifi.
+ *
+ * @hide
*/
public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
/**
+ * Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
+ */
+ public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0;
+
+ /**
+ * Ask the user whether to switch away from bad wifi.
+ */
+ public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1;
+
+ /**
+ * Avoid bad wifi.
+ */
+ public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ NETWORK_AVOID_BAD_WIFI_IGNORE,
+ NETWORK_AVOID_BAD_WIFI_PROMPT,
+ NETWORK_AVOID_BAD_WIFI_AVOID,
+ })
+ public @interface NetworkAvoidBadWifi {}
+
+ /**
* User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be
* overridden by the system based on device or application state. If null, the value
* specified by config_networkMeteredMultipathPreference is used.
+ *
+ * @hide
*/
public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
"network_metered_multipath_preference";
+
+ /**
+ * A list of apps that should go on cellular networks in preference even when higher-priority
+ * networks are connected.
+ *
+ * @hide
+ */
+ public static final String MOBILE_DATA_PREFERRED_APPS = "mobile_data_preferred_apps";
+
+ /**
+ * Get mobile data activity timeout from {@link Settings}.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @param def The default timeout if no setting value.
+ * @return The {@link Duration} of timeout to track mobile data activity.
+ */
+ @NonNull
+ public static Duration getMobileDataActivityTimeout(@NonNull Context context,
+ @NonNull Duration def) {
+ final int timeout = Settings.Global.getInt(
+ context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, (int) def.getSeconds());
+ return Duration.ofSeconds(timeout);
+ }
+
+ /**
+ * Set mobile data activity timeout to {@link Settings}.
+ * Tracking is disabled if set to zero or negative value.
+ *
+ * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be
+ * ignored.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param timeout The mobile data activity timeout.
+ */
+ public static void setMobileDataActivityTimeout(@NonNull Context context,
+ @NonNull Duration timeout) {
+ Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE,
+ (int) timeout.getSeconds());
+ }
+
+ /**
+ * Get wifi data activity timeout from {@link Settings}.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @param def The default timeout if no setting value.
+ * @return The {@link Duration} of timeout to track wifi data activity.
+ */
+ @NonNull
+ public static Duration getWifiDataActivityTimeout(@NonNull Context context,
+ @NonNull Duration def) {
+ final int timeout = Settings.Global.getInt(
+ context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, (int) def.getSeconds());
+ return Duration.ofSeconds(timeout);
+ }
+
+ /**
+ * Set wifi data activity timeout to {@link Settings}.
+ * Tracking is disabled if set to zero or negative value.
+ *
+ * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be
+ * ignored.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param timeout The wifi data activity timeout.
+ */
+ public static void setWifiDataActivityTimeout(@NonNull Context context,
+ @NonNull Duration timeout) {
+ Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI,
+ (int) timeout.getSeconds());
+ }
+
+ /**
+ * Get dns resolver sample validity duration from {@link Settings}.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @param def The default duration if no setting value.
+ * @return The {@link Duration} of sample validity duration to configure for the system DNS
+ * resolver.
+ */
+ @NonNull
+ public static Duration getDnsResolverSampleValidityDuration(@NonNull Context context,
+ @NonNull Duration def) {
+ final int duration = Settings.Global.getInt(context.getContentResolver(),
+ DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, (int) def.getSeconds());
+ return Duration.ofSeconds(duration);
+ }
+
+ /**
+ * Set dns resolver sample validity duration to {@link Settings}. The duration must be a
+ * positive number of seconds.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param duration The sample validity duration.
+ */
+ public static void setDnsResolverSampleValidityDuration(@NonNull Context context,
+ @NonNull Duration duration) {
+ final int time = (int) duration.getSeconds();
+ if (time <= 0) {
+ throw new IllegalArgumentException("Invalid duration");
+ }
+ Settings.Global.putInt(
+ context.getContentResolver(), DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, time);
+ }
+
+ /**
+ * Get dns resolver success threshold percent from {@link Settings}.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @param def The default value if no setting value.
+ * @return The success threshold in percent for use with the system DNS resolver.
+ */
+ public static int getDnsResolverSuccessThresholdPercent(@NonNull Context context, int def) {
+ return Settings.Global.getInt(
+ context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, def);
+ }
+
+ /**
+ * Set dns resolver success threshold percent to {@link Settings}. The threshold percent must
+ * be 0~100.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param percent The success threshold percent.
+ */
+ public static void setDnsResolverSuccessThresholdPercent(@NonNull Context context,
+ @IntRange(from = 0, to = 100) int percent) {
+ if (percent < 0 || percent > 100) {
+ throw new IllegalArgumentException("Percent must be 0~100");
+ }
+ Settings.Global.putInt(
+ context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, percent);
+ }
+
+ /**
+ * Get dns resolver samples range from {@link Settings}.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @return The {@link Range<Integer>} of samples needed for statistics to be considered
+ * meaningful in the system DNS resolver.
+ */
+ @NonNull
+ public static Range<Integer> getDnsResolverSampleRanges(@NonNull Context context) {
+ final int minSamples = Settings.Global.getInt(context.getContentResolver(),
+ DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
+ final int maxSamples = Settings.Global.getInt(context.getContentResolver(),
+ DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
+ return new Range<>(minSamples, maxSamples);
+ }
+
+ /**
+ * Set dns resolver samples range to {@link Settings}.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param range The samples range. The minimum number should be more than 0 and the maximum
+ * number should be less that 64.
+ */
+ public static void setDnsResolverSampleRanges(@NonNull Context context,
+ @NonNull Range<Integer> range) {
+ if (range.getLower() < 0 || range.getUpper() > 64) {
+ throw new IllegalArgumentException("Argument must be 0~64");
+ }
+ Settings.Global.putInt(
+ context.getContentResolver(), DNS_RESOLVER_MIN_SAMPLES, range.getLower());
+ Settings.Global.putInt(
+ context.getContentResolver(), DNS_RESOLVER_MAX_SAMPLES, range.getUpper());
+ }
+
+ /**
+ * Get maximum count (from {@link Settings}) of switching network notifications shown in 24
+ * hours.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @param def The default value if no setting value.
+ * @return The maximum count of notifications shown in 24 hours when switching networks.
+ */
+ public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
+ int def) {
+ return Settings.Global.getInt(
+ context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, def);
+ }
+
+ /**
+ * Set maximum count (to {@link Settings}) of switching network notifications shown in 24 hours.
+ * The count must be at least 0.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param count The maximum count of switching network notifications shown in 24 hours.
+ */
+ public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
+ @IntRange(from = 0) int count) {
+ if (count < 0) {
+ throw new IllegalArgumentException("Count must be 0~10.");
+ }
+ Settings.Global.putInt(
+ context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, count);
+ }
+
+ /**
+ * Get minimum duration (from {@link Settings}) between each switching network notifications.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @param def The default time if no setting value.
+ * @return The minimum duration between notifications when switching networks.
+ */
+ @NonNull
+ public static Duration getNetworkSwitchNotificationRateDuration(@NonNull Context context,
+ @NonNull Duration def) {
+ final int duration = Settings.Global.getInt(context.getContentResolver(),
+ NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, (int) def.toMillis());
+ return Duration.ofMillis(duration);
+ }
+
+ /**
+ * Set minimum duration (to {@link Settings}) between each switching network notifications.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param duration The minimum duration between notifications when switching networks.
+ */
+ public static void setNetworkSwitchNotificationRateDuration(@NonNull Context context,
+ @NonNull Duration duration) {
+ final int time = (int) duration.toMillis();
+ if (time < 0) {
+ throw new IllegalArgumentException("Invalid duration.");
+ }
+ Settings.Global.putInt(context.getContentResolver(),
+ NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, time);
+ }
+
+ /**
+ * Get URL (from {@link Settings}) used for HTTP captive portal detection upon a new connection.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @return The URL used for HTTP captive portal detection upon a new connection.
+ */
+ @Nullable
+ public static String getCaptivePortalHttpUrl(@NonNull Context context) {
+ return Settings.Global.getString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL);
+ }
+
+ /**
+ * Set URL (to {@link Settings}) used for HTTP captive portal detection upon a new connection.
+ * This URL should respond with a 204 response to a GET request to indicate no captive portal is
+ * present. And this URL must be HTTP as redirect responses are used to find captive portal
+ * sign-in pages. If the URL set to null or be incorrect, it will result in captive portal
+ * detection failed and lost the connection.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param url The URL used for HTTP captive portal detection upon a new connection.
+ */
+ public static void setCaptivePortalHttpUrl(@NonNull Context context, @Nullable String url) {
+ Settings.Global.putString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL, url);
+ }
+
+ /**
+ * Get mode (from {@link Settings}) when connecting a network that presents a captive portal.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @param def The default mode if no setting value.
+ * @return The mode when connecting a network that presents a captive portal.
+ */
+ @CaptivePortalMode
+ public static int getCaptivePortalMode(@NonNull Context context,
+ @CaptivePortalMode int def) {
+ return Settings.Global.getInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, def);
+ }
+
+ /**
+ * Set mode (to {@link Settings}) when connecting a network that presents a captive portal.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param mode The mode when connecting a network that presents a captive portal.
+ */
+ public static void setCaptivePortalMode(@NonNull Context context, @CaptivePortalMode int mode) {
+ if (!(mode == CAPTIVE_PORTAL_MODE_IGNORE
+ || mode == CAPTIVE_PORTAL_MODE_PROMPT
+ || mode == CAPTIVE_PORTAL_MODE_AVOID)) {
+ throw new IllegalArgumentException("Invalid captive portal mode");
+ }
+ Settings.Global.putInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, mode);
+ }
+
+ /**
+ * Get the global HTTP proxy applied to the device, or null if none.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @return The {@link ProxyInfo} which build from global http proxy settings.
+ */
+ @Nullable
+ public static ProxyInfo getGlobalProxy(@NonNull Context context) {
+ final String host = Settings.Global.getString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST);
+ final int port = Settings.Global.getInt(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* def */);
+ final String exclusionList = Settings.Global.getString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+ final String pacFileUrl = Settings.Global.getString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC);
+
+ if (TextUtils.isEmpty(host) && TextUtils.isEmpty(pacFileUrl)) {
+ return null; // No global proxy.
+ }
+
+ if (TextUtils.isEmpty(pacFileUrl)) {
+ return ProxyInfo.buildDirectProxy(
+ host, port, ProxyUtils.exclusionStringAsList(exclusionList));
+ } else {
+ return ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl));
+ }
+ }
+
+ /**
+ * Set global http proxy settings from given {@link ProxyInfo}.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from
+ * {@link ProxyInfo#buildPacProxy(Uri)} or
+ * {@link ProxyInfo#buildDirectProxy(String, int, List)}
+ */
+ public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) {
+ final String host = proxyInfo.getHost();
+ final int port = proxyInfo.getPort();
+ final String exclusionList = proxyInfo.getExclusionListAsString();
+ final String pacFileUrl = proxyInfo.getPacFileUrl().toString();
+
+ if (TextUtils.isEmpty(pacFileUrl)) {
+ Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host);
+ Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port);
+ Settings.Global.putString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList);
+ Settings.Global.putString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
+ } else {
+ Settings.Global.putString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
+ Settings.Global.putString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */);
+ Settings.Global.putInt(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */);
+ Settings.Global.putString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */);
+ }
+ }
+
+ /**
+ * Clear all global http proxy settings.
+ *
+ * @param context The {@link Context} to set the setting.
+ */
+ public static void clearGlobalProxy(@NonNull Context context) {
+ Settings.Global.putString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */);
+ Settings.Global.putInt(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */);
+ Settings.Global.putString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */);
+ Settings.Global.putString(
+ context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
+ }
+
+ /**
+ * Get specific private dns provider name from {@link Settings}.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @return The specific private dns provider name, or null if no setting value.
+ */
+ @Nullable
+ public static String getPrivateDnsHostname(@NonNull Context context) {
+ return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_SPECIFIER);
+ }
+
+ /**
+ * Set specific private dns provider name to {@link Settings}.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param specifier The specific private dns provider name.
+ */
+ public static void setPrivateDnsHostname(@NonNull Context context,
+ @Nullable String specifier) {
+ Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_SPECIFIER, specifier);
+ }
+
+ /**
+ * Get default private dns mode from {@link Settings}.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @return The default private dns mode.
+ */
+ @PrivateDnsMode
+ @NonNull
+ public static String getPrivateDnsDefaultMode(@NonNull Context context) {
+ return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE);
+ }
+
+ /**
+ * Set default private dns mode to {@link Settings}.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param mode The default private dns mode. This should be one of the PRIVATE_DNS_MODE_*
+ * constants.
+ */
+ public static void setPrivateDnsDefaultMode(@NonNull Context context,
+ @NonNull @PrivateDnsMode String mode) {
+ if (!(mode == PRIVATE_DNS_MODE_OFF
+ || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC
+ || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
+ throw new IllegalArgumentException("Invalid private dns mode");
+ }
+ Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE, mode);
+ }
+
+ /**
+ * Get duration (from {@link Settings}) to keep a PendingIntent-based request.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @param def The default duration if no setting value.
+ * @return The duration to keep a PendingIntent-based request.
+ */
+ @NonNull
+ public static Duration getConnectivityKeepPendingIntentDuration(@NonNull Context context,
+ @NonNull Duration def) {
+ final int duration = Settings.Secure.getInt(context.getContentResolver(),
+ CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, (int) def.toMillis());
+ return Duration.ofMillis(duration);
+ }
+
+ /**
+ * Set duration (to {@link Settings}) to keep a PendingIntent-based request.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param duration The duration to keep a PendingIntent-based request.
+ */
+ public static void setConnectivityKeepPendingIntentDuration(@NonNull Context context,
+ @NonNull Duration duration) {
+ final int time = (int) duration.toMillis();
+ if (time < 0) {
+ throw new IllegalArgumentException("Invalid duration.");
+ }
+ Settings.Secure.putInt(
+ context.getContentResolver(), CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, time);
+ }
+
+ /**
+ * Read from {@link Settings} whether the mobile data connection should remain active
+ * even when higher priority networks are active.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @param def The default value if no setting value.
+ * @return Whether the mobile data connection should remain active even when higher
+ * priority networks are active.
+ */
+ public static boolean getMobileDataAlwaysOn(@NonNull Context context, boolean def) {
+ final int enable = Settings.Global.getInt(
+ context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (def ? 1 : 0));
+ return (enable != 0) ? true : false;
+ }
+
+ /**
+ * Write into {@link Settings} whether the mobile data connection should remain active
+ * even when higher priority networks are active.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param enable Whether the mobile data connection should remain active even when higher
+ * priority networks are active.
+ */
+ public static void setMobileDataAlwaysOn(@NonNull Context context, boolean enable) {
+ Settings.Global.putInt(
+ context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (enable ? 1 : 0));
+ }
+
+ /**
+ * Read from {@link Settings} whether the wifi data connection should remain active
+ * even when higher priority networks are active.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @param def The default value if no setting value.
+ * @return Whether the wifi data connection should remain active even when higher
+ * priority networks are active.
+ */
+ public static boolean getWifiAlwaysRequested(@NonNull Context context, boolean def) {
+ final int enable = Settings.Global.getInt(
+ context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (def ? 1 : 0));
+ return (enable != 0) ? true : false;
+ }
+
+ /**
+ * Write into {@link Settings} whether the wifi data connection should remain active
+ * even when higher priority networks are active.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param enable Whether the wifi data connection should remain active even when higher
+ * priority networks are active
+ */
+ public static void setWifiAlwaysRequested(@NonNull Context context, boolean enable) {
+ Settings.Global.putInt(
+ context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (enable ? 1 : 0));
+ }
+
+ /**
+ * Get avoid bad wifi setting from {@link Settings}.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @return The setting whether to automatically switch away from wifi networks that lose
+ * internet access.
+ */
+ @NetworkAvoidBadWifi
+ public static int getNetworkAvoidBadWifi(@NonNull Context context) {
+ final String setting =
+ Settings.Global.getString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI);
+ if ("0".equals(setting)) {
+ return NETWORK_AVOID_BAD_WIFI_IGNORE;
+ } else if ("1".equals(setting)) {
+ return NETWORK_AVOID_BAD_WIFI_AVOID;
+ } else {
+ return NETWORK_AVOID_BAD_WIFI_PROMPT;
+ }
+ }
+
+ /**
+ * Set avoid bad wifi setting to {@link Settings}.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param value Whether to automatically switch away from wifi networks that lose internet
+ * access.
+ */
+ public static void setNetworkAvoidBadWifi(@NonNull Context context,
+ @NetworkAvoidBadWifi int value) {
+ final String setting;
+ if (value == NETWORK_AVOID_BAD_WIFI_IGNORE) {
+ setting = "0";
+ } else if (value == NETWORK_AVOID_BAD_WIFI_AVOID) {
+ setting = "1";
+ } else if (value == NETWORK_AVOID_BAD_WIFI_PROMPT) {
+ setting = null;
+ } else {
+ throw new IllegalArgumentException("Invalid avoid bad wifi setting");
+ }
+ Settings.Global.putString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI, setting);
+ }
+
+ /**
+ * Get network metered multipath preference from {@link Settings}.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @return The network metered multipath preference which should be one of
+ * ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value specified
+ * by config_networkMeteredMultipathPreference is used.
+ */
+ @Nullable
+ public static String getNetworkMeteredMultipathPreference(@NonNull Context context) {
+ return Settings.Global.getString(
+ context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE);
+ }
+
+ /**
+ * Set network metered multipath preference to {@link Settings}.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param preference The network metered multipath preference which should be one of
+ * ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value
+ * specified by config_networkMeteredMultipathPreference is used.
+ */
+ public static void setNetworkMeteredMultipathPreference(@NonNull Context context,
+ @NonNull @MultipathPreference String preference) {
+ if (!(Integer.valueOf(preference) == MULTIPATH_PREFERENCE_HANDOVER
+ || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_RELIABILITY
+ || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_PERFORMANCE)) {
+ throw new IllegalArgumentException("Invalid private dns mode");
+ }
+ Settings.Global.putString(
+ context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE, preference);
+ }
+
+ /**
+ * Get the list of apps(from {@link Settings}) that should go on cellular networks in preference
+ * even when higher-priority networks are connected.
+ *
+ * @param context The {@link Context} to query the setting.
+ * @return A list of apps that should go on cellular networks in preference even when
+ * higher-priority networks are connected or null if no setting value.
+ */
+ @Nullable
+ public static String getMobileDataPreferredApps(@NonNull Context context) {
+ return Settings.Secure.getString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS);
+ }
+
+ /**
+ * Set the list of apps(to {@link Settings}) that should go on cellular networks in preference
+ * even when higher-priority networks are connected.
+ *
+ * @param context The {@link Context} to set the setting.
+ * @param list A list of apps that should go on cellular networks in preference even when
+ * higher-priority networks are connected.
+ */
+ public static void setMobileDataPreferredApps(@NonNull Context context, @Nullable String list) {
+ Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS, list);
+ }
}
diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
index 1f66e18..d941d4b 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
@@ -46,4 +46,6 @@
void onRemoveKeepalivePacketFilter(int slot);
void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel);
void onQosCallbackUnregistered(int qosCallbackId);
+ void onNetworkCreated();
+ void onNetworkDestroyed();
}
diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
index cbd6193..26cb1ed 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
@@ -41,4 +41,5 @@
void sendNrQosSessionAvailable(int callbackId, in QosSession session, in NrQosSessionAttributes attributes);
void sendQosSessionLost(int qosCallbackId, in QosSession session);
void sendQosCallbackError(int qosCallbackId, int exceptionType);
+ void sendTeardownDelayMs(int teardownDelayMs);
}
diff --git a/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl b/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl
index 67d2d405..ecfba21 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkOfferCallback.aidl
@@ -22,15 +22,16 @@
* A callback registered with connectivity by network providers together with
* a NetworkOffer.
*
- * When the offer is needed to satisfy some application or system component,
- * connectivity will call onOfferNeeded on this callback. When this happens,
- * the provider should try and bring up the network.
+ * When the network for this offer is needed to satisfy some application or
+ * system component, connectivity will call onNetworkNeeded on this callback.
+ * When this happens, the provider should try and bring up the network.
*
- * When the offer is no longer needed, for example because the application has
- * withdrawn the request or if the request is being satisfied by a network
- * that this offer will never be able to beat, connectivity calls
- * onOfferUnneeded. When this happens, the provider should stop trying to
- * bring up the network, or tear it down if it has already been brought up.
+ * When the network for this offer is no longer needed, for example because
+ * the application has withdrawn the request or if the request is being
+ * satisfied by a network that this offer will never be able to beat,
+ * connectivity calls onNetworkUnneeded. When this happens, the provider
+ * should stop trying to bring up the network, or tear it down if it has
+ * already been brought up.
*
* When NetworkProvider#offerNetwork is called, the provider can expect to
* immediately receive all requests that can be fulfilled by that offer and
@@ -38,25 +39,23 @@
* request is currently outstanding, because no requests have been made that
* can be satisfied by this offer, or because all such requests are already
* satisfied by a better network.
- * onOfferNeeded can be called at any time after registration and until the
+ * onNetworkNeeded can be called at any time after registration and until the
* offer is withdrawn with NetworkProvider#unofferNetwork is called. This
* typically happens when a new network request is filed by an application,
* or when the network satisfying a request disconnects and this offer now
- * stands a chance to be the best network for it.
+ * stands a chance to supply the best network for it.
*
* @hide
*/
oneway interface INetworkOfferCallback {
/**
- * Informs the registrant that the offer is needed to fulfill this request.
+ * Called when a network for this offer is needed to fulfill this request.
* @param networkRequest the request to satisfy
- * @param providerId the ID of the provider currently satisfying
- * this request, or NetworkProvider.ID_NONE if none.
*/
- void onOfferNeeded(in NetworkRequest networkRequest, int providerId);
+ void onNetworkNeeded(in NetworkRequest networkRequest);
/**
- * Informs the registrant that the offer is no longer needed to fulfill this request.
+ * Informs the registrant that the offer is no longer valuable to fulfill this request.
*/
- void onOfferUnneeded(in NetworkRequest networkRequest);
+ void onNetworkUnneeded(in NetworkRequest networkRequest);
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index f7cd4f6..c57da53 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -185,6 +185,20 @@
public static final int EVENT_UNDERLYING_NETWORKS_CHANGED = BASE + 5;
/**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current value of the teardown
+ * delay.
+ * arg1 = teardown delay in milliseconds
+ * @hide
+ */
+ public static final int EVENT_TEARDOWN_DELAY_CHANGED = BASE + 6;
+
+ /**
+ * The maximum value for the teardown delay, in milliseconds.
+ * @hide
+ */
+ public static final int MAX_TEARDOWN_DELAY_MS = 5000;
+
+ /**
* Sent by ConnectivityService to the NetworkAgent to inform the agent of the
* networks status - whether we could use the network or could not, due to
* either a bad network configuration (no internet link) or captive portal.
@@ -197,7 +211,6 @@
*/
public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
-
/**
* Network validation suceeded.
* Corresponds to {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}.
@@ -362,6 +375,22 @@
*/
public static final int CMD_UNREGISTER_QOS_CALLBACK = BASE + 21;
+ /**
+ * Sent by ConnectivityService to {@link NetworkAgent} to inform the agent that its native
+ * network was created and the Network object is now valid.
+ *
+ * @hide
+ */
+ public static final int CMD_NETWORK_CREATED = BASE + 22;
+
+ /**
+ * Sent by ConnectivityService to {@link NetworkAgent} to inform the agent that its native
+ * network was destroyed.
+ *
+ * @hide
+ */
+ public static final int CMD_NETWORK_DESTROYED = BASE + 23;
+
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
config.legacyTypeName, config.legacySubTypeName);
@@ -561,6 +590,14 @@
msg.arg1 /* QoS callback id */);
break;
}
+ case CMD_NETWORK_CREATED: {
+ onNetworkCreated();
+ break;
+ }
+ case CMD_NETWORK_DESTROYED: {
+ onNetworkDestroyed();
+ break;
+ }
}
}
}
@@ -701,6 +738,16 @@
mHandler.sendMessage(mHandler.obtainMessage(
CMD_UNREGISTER_QOS_CALLBACK, qosCallbackId, 0, null));
}
+
+ @Override
+ public void onNetworkCreated() {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_CREATED));
+ }
+
+ @Override
+ public void onNetworkDestroyed() {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_DESTROYED));
+ }
}
/**
@@ -817,6 +864,29 @@
}
/**
+ * Sets the value of the teardown delay.
+ *
+ * The teardown delay is the time between when the network disconnects and when the native
+ * network corresponding to this {@code NetworkAgent} is destroyed. By default, the native
+ * network is destroyed immediately. If {@code teardownDelayMs} is non-zero, then when this
+ * network disconnects, the system will instead immediately mark the network as restricted
+ * and unavailable to unprivileged apps, but will defer destroying the native network until the
+ * teardown delay timer expires.
+ *
+ * The interfaces in use by this network will remain in use until the native network is
+ * destroyed and cannot be reused until {@link #onNetworkDestroyed()} is called.
+ *
+ * This method may be called at any time while the network is connected. It has no effect if
+ * the network is already disconnected and the teardown delay timer is running.
+ *
+ * @param teardownDelayMs the teardown delay to set, or 0 to disable teardown delay.
+ */
+ public void setTeardownDelayMs(
+ @IntRange(from = 0, to = MAX_TEARDOWN_DELAY_MS) int teardownDelayMs) {
+ queueOrSendMessage(reg -> reg.sendTeardownDelayMs(teardownDelayMs));
+ }
+
+ /**
* Change the legacy subtype of this network agent.
*
* This is only for backward compatibility and should not be used by non-legacy network agents,
@@ -1011,6 +1081,17 @@
}
/**
+ * Called when ConnectivityService has successfully created this NetworkAgent's native network.
+ */
+ public void onNetworkCreated() {}
+
+
+ /**
+ * Called when ConnectivityService has successfully destroy this NetworkAgent's native network.
+ */
+ public void onNetworkDestroyed() {}
+
+ /**
* Requests that the network hardware send the specified packet at the specified interval.
*
* @param slot the hardware slot on which to start the keepalive.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index f50b018..4f95ccc 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -273,6 +273,9 @@
NET_CAPABILITY_VEHICLE_INTERNAL,
NET_CAPABILITY_NOT_VCN_MANAGED,
NET_CAPABILITY_ENTERPRISE,
+ NET_CAPABILITY_VSIM,
+ NET_CAPABILITY_BIP,
+ NET_CAPABILITY_HEAD_UNIT,
})
public @interface NetCapability { }
@@ -492,8 +495,27 @@
*/
public static final int NET_CAPABILITY_ENTERPRISE = 29;
+ /**
+ * Indicates that this network has ability to access the carrier's Virtual Sim service.
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_VSIM = 30;
+
+ /**
+ * Indicates that this network has ability to support Bearer Independent Protol.
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_BIP = 31;
+
+ /**
+ * Indicates that this network is connected to an automotive head unit.
+ */
+ public static final int NET_CAPABILITY_HEAD_UNIT = 32;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_ENTERPRISE;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_HEAD_UNIT;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -511,7 +533,10 @@
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
| (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
- | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED)
+ // The value of NET_CAPABILITY_HEAD_UNIT is 32, which cannot use int to do bit shift,
+ // otherwise there will be an overflow. Use long to do bit shift instead.
+ | (1L << NET_CAPABILITY_HEAD_UNIT);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -851,6 +876,7 @@
TRANSPORT_WIFI_AWARE,
TRANSPORT_LOWPAN,
TRANSPORT_TEST,
+ TRANSPORT_USB,
})
public @interface Transport { }
@@ -897,10 +923,15 @@
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int TRANSPORT_TEST = 7;
+ /**
+ * Indicates this network uses a USB transport.
+ */
+ public static final int TRANSPORT_USB = 8;
+
/** @hide */
public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
/** @hide */
- public static final int MAX_TRANSPORT = TRANSPORT_TEST;
+ public static final int MAX_TRANSPORT = TRANSPORT_USB;
/** @hide */
public static boolean isValidTransport(@Transport int transportType) {
@@ -915,7 +946,8 @@
"VPN",
"WIFI_AWARE",
"LOWPAN",
- "TEST"
+ "TEST",
+ "USB"
};
/**
@@ -2089,6 +2121,9 @@
case NET_CAPABILITY_VEHICLE_INTERNAL: return "VEHICLE_INTERNAL";
case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED";
case NET_CAPABILITY_ENTERPRISE: return "ENTERPRISE";
+ case NET_CAPABILITY_VSIM: return "VSIM";
+ case NET_CAPABILITY_BIP: return "BIP";
+ case NET_CAPABILITY_HEAD_UNIT: return "HEAD_UNIT";
default: return Integer.toString(capability);
}
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkProvider.java b/packages/Connectivity/framework/src/android/net/NetworkProvider.java
index d5b5c9b..cfb7325 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkProvider.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkProvider.java
@@ -168,12 +168,17 @@
}
/** @hide */
- // TODO : make @SystemApi when the impl is complete
+ @SystemApi
public interface NetworkOfferCallback {
- /** Called by the system when this offer is needed to satisfy some networking request. */
- void onOfferNeeded(@NonNull NetworkRequest request, int providerId);
- /** Called by the system when this offer is no longer needed. */
- void onOfferUnneeded(@NonNull NetworkRequest request);
+ /**
+ * Called by the system when a network for this offer is needed to satisfy some
+ * networking request.
+ */
+ void onNetworkNeeded(@NonNull NetworkRequest request);
+ /**
+ * Called by the system when this offer is no longer valuable for this request.
+ */
+ void onNetworkUnneeded(@NonNull NetworkRequest request);
}
private class NetworkOfferCallbackProxy extends INetworkOfferCallback.Stub {
@@ -187,14 +192,13 @@
}
@Override
- public void onOfferNeeded(final @NonNull NetworkRequest request,
- final int providerId) {
- mExecutor.execute(() -> callback.onOfferNeeded(request, providerId));
+ public void onNetworkNeeded(final @NonNull NetworkRequest request) {
+ mExecutor.execute(() -> callback.onNetworkNeeded(request));
}
@Override
- public void onOfferUnneeded(final @NonNull NetworkRequest request) {
- mExecutor.execute(() -> callback.onOfferUnneeded(request));
+ public void onNetworkUnneeded(final @NonNull NetworkRequest request) {
+ mExecutor.execute(() -> callback.onNetworkUnneeded(request));
}
}
@@ -213,48 +217,51 @@
}
/**
- * Register or update an offer for network with the passed caps and score.
+ * Register or update an offer for network with the passed capabilities and score.
*
- * A NetworkProvider's job is to provide networks. This function is how a provider tells the
+ * A NetworkProvider's role is to provide networks. This method is how a provider tells the
* connectivity stack what kind of network it may provide. The score and caps arguments act
- * as filters that the connectivity stack uses to tell when the offer is necessary. When an
- * offer might be advantageous over existing networks, the provider will receive a call to
- * the associated callback's {@link NetworkOfferCallback#onOfferNeeded} method. The provider
- * should then try to bring up this network. When an offer is no longer needed, the stack
- * will inform the provider by calling {@link NetworkOfferCallback#onOfferUnneeded}. The
+ * as filters that the connectivity stack uses to tell when the offer is valuable. When an
+ * offer might be preferred over existing networks, the provider will receive a call to
+ * the associated callback's {@link NetworkOfferCallback#onNetworkNeeded} method. The provider
+ * should then try to bring up this network. When an offer is no longer useful, the stack
+ * will inform the provider by calling {@link NetworkOfferCallback#onNetworkUnneeded}. The
* provider should stop trying to bring up such a network, or disconnect it if it already has
* one.
*
- * The stack determines what offers are needed according to what networks are currently
+ * The stack determines what offers are valuable according to what networks are currently
* available to the system, and what networking requests are made by applications. If an
- * offer looks like it could be a better choice than any existing network for any particular
- * request, that's when the stack decides the offer is needed. If the current networking
- * requests are all satisfied by networks that this offer can't possibly be a better match
- * for, that's when the offer is unneeded. An offer starts off as unneeded ; the provider
- * should not try to bring up the network until {@link NetworkOfferCallback#onOfferNeeded}
- * is called.
+ * offer looks like it could connect a better network than any existing network for any
+ * particular request, that's when the stack decides the network is needed. If the current
+ * networking requests are all satisfied by networks that this offer couldn't possibly be a
+ * better match for, that's when the offer is no longer valuable. An offer starts out as
+ * unneeded ; the provider should not try to bring up the network until
+ * {@link NetworkOfferCallback#onNetworkNeeded} is called.
*
* Note that the offers are non-binding to the providers, in particular because providers
* often don't know if they will be able to bring up such a network at any given time. For
- * example, no wireless network may be in range when the offer is needed. This is fine and
- * expected ; the provider should simply continue to try to bring up the network and do so
+ * example, no wireless network may be in range when the offer would be valuable. This is fine
+ * and expected ; the provider should simply continue to try to bring up the network and do so
* if/when it becomes possible. In the mean time, the stack will continue to satisfy requests
* with the best network currently available, or if none, keep the apps informed that no
* network can currently satisfy this request. When/if the provider can bring up the network,
* the connectivity stack will match it against requests, and inform interested apps of the
* availability of this network. This may, in turn, render the offer of some other provider
- * unneeded if all requests it used to satisfy are now better served by this network.
+ * low-value if all requests it used to satisfy are now better served by this network.
*
* A network can become unneeded for a reason like the above : whether the provider managed
* to bring up the offered network after it became needed or not, some other provider may
- * bring up a better network than this one, making this offer unneeded. A network may also
+ * bring up a better network than this one, making this network unneeded. A network may also
* become unneeded if the application making the request withdrew it (for example, after it
* is done transferring data, or if the user canceled an operation).
*
* The capabilities and score act as filters as to what requests the provider will see.
* They are not promises, but for best performance, the providers should strive to put
- * as much known information as possible in the offer. For capabilities in particular, it
- * should put all NetworkAgent-managed capabilities a network may have, even if it doesn't
+ * as much known information as possible in the offer. For the score, it should put as
+ * strong a score as the networks will have, since this will filter what requests the
+ * provider sees – it's not a promise, it only serves to avoid sending requests that
+ * the provider can't ever hope to satisfy better than any current network. For capabilities,
+ * it should put all NetworkAgent-managed capabilities a network may have, even if it doesn't
* have them at first. This applies to INTERNET, for example ; if a provider thinks the
* network it can bring up for this offer may offer Internet access it should include the
* INTERNET bit. It's fine if the brought up network ends up not actually having INTERNET.
@@ -267,9 +274,9 @@
*
* @hide
*/
- // TODO : make @SystemApi when the impl is complete
+ @SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
- public void offerNetwork(@NonNull final NetworkScore score,
+ public void registerNetworkOffer(@NonNull final NetworkScore score,
@NonNull final NetworkCapabilities caps, @NonNull final Executor executor,
@NonNull final NetworkOfferCallback callback) {
// Can't offer a network with a provider that is not yet registered or already unregistered.
@@ -306,9 +313,9 @@
*
* @hide
*/
- // TODO : make @SystemApi when the impl is complete
+ @SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
- public void unofferNetwork(final @NonNull NetworkOfferCallback callback) {
+ public void unregisterNetworkOffer(final @NonNull NetworkOfferCallback callback) {
final NetworkOfferCallbackProxy proxy = findProxyForCallback(callback);
if (null == proxy) return;
mProxies.remove(proxy);
diff --git a/packages/Connectivity/framework/src/android/net/NetworkScore.java b/packages/Connectivity/framework/src/android/net/NetworkScore.java
index 6584993..9786b09 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkScore.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkScore.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
@@ -23,6 +24,9 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Object representing the quality of a network as perceived by the user.
*
@@ -36,6 +40,17 @@
// a migration.
private final int mLegacyInt;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ KEEP_CONNECTED_NONE,
+ KEEP_CONNECTED_FOR_HANDOVER
+ })
+ public @interface KeepConnectedReason { }
+
+ public static final int KEEP_CONNECTED_NONE = 0;
+ public static final int KEEP_CONNECTED_FOR_HANDOVER = 1;
+
// Agent-managed policies
// TODO : add them here, starting from 1
/** @hide */
@@ -46,15 +61,20 @@
// Bitmask of all the policies applied to this score.
private final long mPolicies;
+ private final int mKeepConnectedReason;
+
/** @hide */
- NetworkScore(final int legacyInt, final long policies) {
+ NetworkScore(final int legacyInt, final long policies,
+ @KeepConnectedReason final int keepConnectedReason) {
mLegacyInt = legacyInt;
mPolicies = policies;
+ mKeepConnectedReason = keepConnectedReason;
}
private NetworkScore(@NonNull final Parcel in) {
mLegacyInt = in.readInt();
mPolicies = in.readLong();
+ mKeepConnectedReason = in.readInt();
}
public int getLegacyInt() {
@@ -62,6 +82,13 @@
}
/**
+ * Returns the keep-connected reason, or KEEP_CONNECTED_NONE.
+ */
+ public int getKeepConnectedReason() {
+ return mKeepConnectedReason;
+ }
+
+ /**
* @return whether this score has a particular policy.
*
* @hide
@@ -80,6 +107,7 @@
public void writeToParcel(@NonNull final Parcel dest, final int flags) {
dest.writeInt(mLegacyInt);
dest.writeLong(mPolicies);
+ dest.writeInt(mKeepConnectedReason);
}
@Override
@@ -108,6 +136,7 @@
private static final long POLICY_NONE = 0L;
private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE;
private int mLegacyInt = INVALID_LEGACY_INT;
+ private int mKeepConnectedReason = KEEP_CONNECTED_NONE;
/**
* Sets the legacy int for this score.
@@ -124,12 +153,23 @@
}
/**
+ * Set the keep-connected reason.
+ *
+ * This can be reset by calling it again with {@link KEEP_CONNECTED_NONE}.
+ */
+ @NonNull
+ public Builder setKeepConnectedReason(@KeepConnectedReason final int reason) {
+ mKeepConnectedReason = reason;
+ return this;
+ }
+
+ /**
* Builds this NetworkScore.
* @return The built NetworkScore object.
*/
@NonNull
public NetworkScore build() {
- return new NetworkScore(mLegacyInt, POLICY_NONE);
+ return new NetworkScore(mLegacyInt, POLICY_NONE, mKeepConnectedReason);
}
}
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
index 16a49bc..2679b62 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -42,6 +42,9 @@
* {@hide}
*/
public class NetworkUtils {
+ static {
+ System.loadLibrary("framework-connectivity-jni");
+ }
private static final String TAG = "NetworkUtils";
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index cd8f4c0..ba83a44 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -17,11 +17,14 @@
package android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.util.Objects;
@@ -38,8 +41,26 @@
/** Type of this VPN. */
public final int type;
- public VpnTransportInfo(int type) {
+ @Nullable
+ public final String sessionId;
+
+ @Override
+ public long getApplicableRedactions() {
+ return REDACT_FOR_NETWORK_SETTINGS;
+ }
+
+ /**
+ * Create a copy of a {@link VpnTransportInfo} with the sessionId redacted if necessary.
+ */
+ @NonNull
+ public VpnTransportInfo makeCopy(long redactions) {
+ return new VpnTransportInfo(type,
+ ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : sessionId);
+ }
+
+ public VpnTransportInfo(int type, @Nullable String sessionId) {
this.type = type;
+ this.sessionId = sessionId;
}
@Override
@@ -47,17 +68,17 @@
if (!(o instanceof VpnTransportInfo)) return false;
VpnTransportInfo that = (VpnTransportInfo) o;
- return this.type == that.type;
+ return (this.type == that.type) && TextUtils.equals(this.sessionId, that.sessionId);
}
@Override
public int hashCode() {
- return Objects.hash(type);
+ return Objects.hash(type, sessionId);
}
@Override
public String toString() {
- return String.format("VpnTransportInfo{type=%d}", type);
+ return String.format("VpnTransportInfo{type=%d, sessionId=%s}", type, sessionId);
}
@Override
@@ -68,12 +89,13 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(type);
+ dest.writeString(sessionId);
}
public static final @NonNull Creator<VpnTransportInfo> CREATOR =
new Creator<VpnTransportInfo>() {
public VpnTransportInfo createFromParcel(Parcel in) {
- return new VpnTransportInfo(in.readInt());
+ return new VpnTransportInfo(in.readInt(), in.readString());
}
public VpnTransportInfo[] newArray(int size) {
return new VpnTransportInfo[size];
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index 9d1bb0f..518f198 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -72,7 +72,7 @@
"ServiceConnectivityResources",
],
static_libs: [
- "dnsresolver_aidl_interface-V7-java",
+ "dnsresolver_aidl_interface-V8-java",
"modules-utils-os",
"net-utils-device-common",
"net-utils-framework-common",
@@ -83,7 +83,6 @@
"service-connectivity-protos",
],
apex_available: [
- "//apex_available:platform",
"com.android.tethering",
],
}
@@ -100,7 +99,6 @@
],
libs: ["libprotobuf-java-nano"],
apex_available: [
- "//apex_available:platform",
"com.android.tethering",
],
}
@@ -115,7 +113,7 @@
],
jarjar_rules: "jarjar-rules.txt",
apex_available: [
- "//apex_available:platform",
+ "//apex_available:platform", // For arc-services
"com.android.tethering",
],
}
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-af/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-af/strings.xml
index 68720d5..550ab8a 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-af/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-af/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Meld aan by Wi-Fi-netwerk"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Meld by netwerk aan"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het geen internettoegang nie"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tik vir opsies"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Selnetwerk het nie internettoegang nie"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Netwerk het nie internettoegang nie"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het beperkte konnektiwiteit"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tik om in elk geval te koppel"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Het oorgeskakel na <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Toestel gebruik <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internettoegang het nie. Heffings kan geld."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Het oorgeskakel van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"\'n onbekende netwerktipe"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Stelselkonnektiwiteithulpbronne"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Meld aan by Wi-Fi-netwerk"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Meld by netwerk aan"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het geen internettoegang nie"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tik vir opsies"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Selnetwerk het nie internettoegang nie"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Netwerk het nie internettoegang nie"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> het beperkte konnektiwiteit"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tik om in elk geval te koppel"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Het oorgeskakel na <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Toestel gebruik <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internettoegang het nie. Heffings kan geld."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Het oorgeskakel van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiele data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobiele data"</item>
+ <item msgid="6341719431034774569">"Wi-fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"\'n onbekende netwerktipe"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-am/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-am/strings.xml
index 78d9283..7f1a9db 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-am/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-am/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ወደ Wi-Fi አውታረ መረብ በመለያ ግባ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ወደ አውታረ መረብ በመለያ ይግቡ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ምንም የበይነ መረብ መዳረሻ የለም"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ለአማራጮች መታ ያድርጉ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"የተንቀሳቃሽ ስልክ አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> የተገደበ ግንኙነት አለው"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ለማንኛውም ለማገናኘት መታ ያድርጉ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"ወደ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ተቀይሯል"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ምንም ዓይነት የበይነመረብ ግንኙነት በማይኖረው ጊዜ መሣሪያዎች <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ን ይጠቀማሉ። ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ።"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"ከ<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ወደ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ተቀይሯል"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"አንድ ያልታወቀ አውታረ መረብ ዓይነት"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"የስርዓት ግንኙነት መርጃዎች"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ወደ Wi-Fi አውታረ መረብ በመለያ ግባ"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ወደ አውታረ መረብ በመለያ ይግቡ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ምንም የበይነ መረብ መዳረሻ የለም"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ለአማራጮች መታ ያድርጉ"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"የተንቀሳቃሽ ስልክ አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"አውታረ መረብ የበይነመረብ መዳረሻ የለውም"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> የተገደበ ግንኙነት አለው"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ለማንኛውም ለማገናኘት መታ ያድርጉ"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"ወደ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ተቀይሯል"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ምንም ዓይነት የበይነመረብ ግንኙነት በማይኖረው ጊዜ መሣሪያዎች <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ን ይጠቀማሉ። ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ።"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"ከ<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ወደ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ተቀይሯል"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"የተንቀሳቃሽ ስልክ ውሂብ"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"ብሉቱዝ"</item>
- <item msgid="1616528372438698248">"ኤተርኔት"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"የተንቀሳቃሽ ስልክ ውሂብ"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"ብሉቱዝ"</item>
+ <item msgid="1160736166977503463">"ኢተርኔት"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"አንድ ያልታወቀ አውታረ መረብ ዓይነት"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ar/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ar/strings.xml
index 8698a7e..b7a62c5 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ar/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ar/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"تسجيل الدخول إلى شبكة Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"تسجيل الدخول إلى الشبكة"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"لا يتوفّر في <xliff:g id="NETWORK_SSID">%1$s</xliff:g> إمكانية الاتصال بالإنترنت."</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"انقر للحصول على الخيارات."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"شبكة الجوّال هذه غير متصلة بالإنترنت"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"الشبكة غير متصلة بالإنترنت"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"إمكانية اتصال <xliff:g id="NETWORK_SSID">%1$s</xliff:g> محدودة."</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"يمكنك النقر للاتصال على أي حال."</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"تم التبديل إلى <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"يستخدم الجهاز <xliff:g id="NEW_NETWORK">%1$s</xliff:g> عندما لا يتوفر اتصال بالإنترنت في شبكة <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>، ويمكن أن يتم فرض رسوم مقابل ذلك."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"تم التبديل من <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> إلى <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"نوع شبكة غير معروف"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"مصادر إمكانية اتصال الخادم"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"تسجيل الدخول إلى شبكة Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"تسجيل الدخول إلى الشبكة"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"لا يتوفّر في <xliff:g id="NETWORK_SSID">%1$s</xliff:g> إمكانية الاتصال بالإنترنت."</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"انقر للحصول على الخيارات."</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"شبكة الجوّال هذه غير متصلة بالإنترنت"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"الشبكة غير متصلة بالإنترنت"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"إمكانية اتصال <xliff:g id="NETWORK_SSID">%1$s</xliff:g> محدودة."</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"يمكنك النقر للاتصال على أي حال."</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"تم التبديل إلى <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"يستخدم الجهاز <xliff:g id="NEW_NETWORK">%1$s</xliff:g> عندما لا يتوفر اتصال بالإنترنت في شبكة <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>، ويمكن أن يتم فرض رسوم مقابل ذلك."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"تم التبديل من <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> إلى <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"بيانات الجوّال"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"بلوتوث"</item>
- <item msgid="1616528372438698248">"إيثرنت"</item>
- <item msgid="9177085807664964627">"شبكة افتراضية خاصة (VPN)"</item>
+ <item msgid="5454013645032700715">"بيانات الجوّال"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"بلوتوث"</item>
+ <item msgid="1160736166977503463">"إيثرنت"</item>
+ <item msgid="7347618872551558605">"شبكة افتراضية خاصة (VPN)"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"نوع شبكة غير معروف"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-as/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-as/strings.xml
index 10b234a..cf8e6ac 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-as/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-as/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ৱাই-ফাই নেটৱৰ্কত ছাইন ইন কৰক"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"নেটৱৰ্কত ছাইন ইন কৰক"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"অধিক বিকল্পৰ বাবে টিপক"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ম’বাইল নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ব্যক্তিগত DNS ছাৰ্ভাৰ এক্সেছ কৰিব নোৱাৰি"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ সকলো সেৱাৰ এক্সেছ নাই"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"যিকোনো প্ৰকাৰে সংযোগ কৰিবলৈ টিপক"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ’ল"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"যেতিয়া <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ত ইণ্টাৰনেট নাথাকে, তেতিয়া ডিভাইচে <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ক ব্যৱহাৰ কৰে। মাচুল প্ৰযোজ্য হ\'ব পাৰে।"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ’ল"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"অজ্ঞাত প্ৰকাৰৰ নেটৱৰ্ক"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ছিষ্টেম সংযোগৰ উৎস"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ৱাই-ফাই নেটৱৰ্কত ছাইন ইন কৰক"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"নেটৱৰ্কত ছাইন ইন কৰক"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"অধিক বিকল্পৰ বাবে টিপক"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"ম’বাইল নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"নেটৱৰ্কৰ কোনো ইণ্টাৰনেটৰ এক্সেছ নাই"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ব্যক্তিগত DNS ছাৰ্ভাৰ এক্সেছ কৰিব নোৱাৰি"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ৰ সকলো সেৱাৰ এক্সেছ নাই"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"যিকোনো প্ৰকাৰে সংযোগ কৰিবলৈ টিপক"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ’ল"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"যেতিয়া <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ত ইণ্টাৰনেট নাথাকে, তেতিয়া ডিভাইচে <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ক ব্যৱহাৰ কৰে। মাচুল প্ৰযোজ্য হ\'ব পাৰে।"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ’ল"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ম’বাইল ডেটা"</item>
- <item msgid="5520925862115353992">"ৱাই-ফাই"</item>
- <item msgid="1055487873974272842">"ব্লুটুথ"</item>
- <item msgid="1616528372438698248">"ইথাৰনেট"</item>
- <item msgid="9177085807664964627">"ভিপিএন"</item>
+ <item msgid="5454013645032700715">"ম’বাইল ডেটা"</item>
+ <item msgid="6341719431034774569">"ৱাই-ফাই"</item>
+ <item msgid="5081440868800877512">"ব্লুটুথ"</item>
+ <item msgid="1160736166977503463">"ইথাৰনেট"</item>
+ <item msgid="7347618872551558605">"ভিপিএন"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"অজ্ঞাত প্ৰকাৰৰ নেটৱৰ্ক"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-az/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-az/strings.xml
index d75a204..7e927ed 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-az/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-az/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi şəbəkəsinə daxil ol"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Şəbəkəyə daxil olun"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> üçün internet girişi əlçatan deyil"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Seçimlər üçün tıklayın"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobil şəbəkənin internetə girişi yoxdur"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Şəbəkənin internetə girişi yoxdur"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Özəl DNS serverinə giriş mümkün deyil"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> bağlantını məhdudlaşdırdı"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"İstənilən halda klikləyin"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> şəbəkə növünə keçirildi"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> şəbəkəsinin internetə girişi olmadıqda, cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> şəbəkəsini istifadə edir. Xidmət haqqı tutula bilər."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> şəbəkəsindən <xliff:g id="NEW_NETWORK">%2$s</xliff:g> şəbəkəsinə keçirildi"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"naməlum şəbəkə növü"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Sistem Bağlantı Resursları"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi şəbəkəsinə daxil ol"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Şəbəkəyə daxil olun"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> üçün internet girişi əlçatan deyil"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Seçimlər üçün tıklayın"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobil şəbəkənin internetə girişi yoxdur"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Şəbəkənin internetə girişi yoxdur"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Özəl DNS serverinə giriş mümkün deyil"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> bağlantını məhdudlaşdırdı"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"İstənilən halda klikləyin"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> şəbəkə növünə keçirildi"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> şəbəkəsinin internetə girişi olmadıqda, cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> şəbəkəsini istifadə edir. Xidmət haqqı tutula bilər."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> şəbəkəsindən <xliff:g id="NEW_NETWORK">%2$s</xliff:g> şəbəkəsinə keçirildi"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobil data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobil data"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"naməlum şəbəkə növü"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml
index 11e4957..3f1b976 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-b+sr+Latn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavljivanje na WiFi mrežu"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prijavite se na mrežu"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dodirnite za opcije"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilna mreža nema pristup internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mreža nema pristup internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Pristup privatnom DNS serveru nije uspeo"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu vezu"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dodirnite da biste se ipak povezali"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Prešli ste na tip mreže <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Uređaj koristi tip mreže <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kada tip mreže <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu. Možda će se naplaćivati troškovi."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Prešli ste sa tipa mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na tip mreže <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznat tip mreže"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resursi za povezivanje sa sistemom"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prijavljivanje na WiFi mrežu"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Prijavite se na mrežu"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Dodirnite za opcije"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilna mreža nema pristup internetu"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Mreža nema pristup internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Pristup privatnom DNS serveru nije uspeo"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu vezu"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Dodirnite da biste se ipak povezali"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Prešli ste na tip mreže <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Uređaj koristi tip mreže <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kada tip mreže <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu. Možda će se naplaćivati troškovi."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Prešli ste sa tipa mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na tip mreže <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilni podaci"</item>
- <item msgid="5520925862115353992">"WiFi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Eternet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobilni podaci"</item>
+ <item msgid="6341719431034774569">"WiFi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Eternet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"nepoznat tip mreže"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-be/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-be/strings.xml
index 6b0b1f1..21edf24 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-be/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-be/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Уваход у сетку Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Увайдзіце ў сетку"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> не мае доступу ў інтэрнэт"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Дакраніцеся, каб убачыць параметры"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мабільная сетка не мае доступу ў інтэрнэт"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Сетка не мае доступу ў інтэрнэт"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> мае абмежаваную магчымасць падключэння"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Націсніце, каб падключыцца"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Выкананы пераход да <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Прылада выкарыстоўвае сетку <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, калі ў сетцы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма доступу да інтэрнэту. Можа спаганяцца плата."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Выкананы пераход з <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> да <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"невядомы тып сеткі"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Рэсурсы для падключэння да сістэмы"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Уваход у сетку Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Увайдзіце ў сетку"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> не мае доступу ў інтэрнэт"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Дакраніцеся, каб убачыць параметры"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Мабільная сетка не мае доступу ў інтэрнэт"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Сетка не мае доступу ў інтэрнэт"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> мае абмежаваную магчымасць падключэння"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Націсніце, каб падключыцца"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Выкананы пераход да <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Прылада выкарыстоўвае сетку <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, калі ў сетцы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма доступу да інтэрнэту. Можа спаганяцца плата."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Выкананы пераход з <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> да <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мабільная перадача даных"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"мабільная перадача даных"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"невядомы тып сеткі"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-bg/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bg/strings.xml
index 6427bd0..c3c2d72 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-bg/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bg/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Влизане в Wi-Fi мрежа"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Вход в мрежата"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> няма достъп до интернет"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Докоснете за опции"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилната мрежа няма достъп до интернет"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Мрежата няма достъп до интернет"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Не може да се осъществи достъп до частния DNS сървър"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена свързаност"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Докоснете, за да се свържете въпреки това"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Превключи се към <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Устройството използва <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, когато <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма достъп до интернет. Възможно е да бъдете таксувани."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Превключи се от <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> към <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"неизвестен тип мрежа"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ресурси за свързаността на системата"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Влизане в Wi-Fi мрежа"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Вход в мрежата"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> няма достъп до интернет"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Докоснете за опции"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Мобилната мрежа няма достъп до интернет"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Мрежата няма достъп до интернет"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Не може да се осъществи достъп до частния DNS сървър"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена свързаност"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Докоснете, за да се свържете въпреки това"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Превключи се към <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Устройството използва <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, когато <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма достъп до интернет. Възможно е да бъдете таксувани."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Превключи се от <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> към <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобилни данни"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"виртуална частна мрежа (VPN)"</item>
+ <item msgid="5454013645032700715">"мобилни данни"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"неизвестен тип мрежа"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-bn/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bn/strings.xml
index 74d4cd6..0f693bd 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-bn/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ওয়াই-ফাই নেটওয়ার্কে সাইন-ইন করুন"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"নেটওয়ার্কে সাইন-ইন করুন"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর ইন্টারনেটে অ্যাক্সেস নেই"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"বিকল্পগুলির জন্য আলতো চাপুন"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"মোবাইল নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর সীমিত কানেক্টিভিটি আছে"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"তবুও কানেক্ট করতে ট্যাপ করুন"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> এ পাল্টানো হয়েছে"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> এ ইন্টারনেট অ্যাক্সেস না থাকলে <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ব্যবহার করা হয়৷ ডেটা চার্জ প্রযোজ্য৷"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> থেকে <xliff:g id="NEW_NETWORK">%2$s</xliff:g> এ পাল্টানো হয়েছে"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"এই নেটওয়ার্কের প্রকার অজানা"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"সিস্টেম কানেক্টিভিটি রিসোর্সেস"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ওয়াই-ফাই নেটওয়ার্কে সাইন-ইন করুন"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"নেটওয়ার্কে সাইন-ইন করুন"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর ইন্টারনেটে অ্যাক্সেস নেই"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"বিকল্পগুলির জন্য আলতো চাপুন"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"মোবাইল নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"নেটওয়ার্কে কোনও ইন্টারনেট অ্যাক্সেস নেই"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-এর সীমিত কানেক্টিভিটি আছে"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"তবুও কানেক্ট করতে ট্যাপ করুন"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> এ পাল্টানো হয়েছে"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> এ ইন্টারনেট অ্যাক্সেস না থাকলে <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ব্যবহার করা হয়৷ ডেটা চার্জ প্রযোজ্য৷"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> থেকে <xliff:g id="NEW_NETWORK">%2$s</xliff:g> এ পাল্টানো হয়েছে"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"মোবাইল ডেটা"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"ব্লুটুথ"</item>
- <item msgid="1616528372438698248">"ইথারনেট"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"মোবাইল ডেটা"</item>
+ <item msgid="6341719431034774569">"ওয়াই-ফাই"</item>
+ <item msgid="5081440868800877512">"ব্লুটুথ"</item>
+ <item msgid="1160736166977503463">"ইথারনেট"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"এই নেটওয়ার্কের ধরন অজানা"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-bs/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bs/strings.xml
index 19f6e1a..33d6ed9 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-bs/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-bs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavljivanje na WiFi mrežu"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prijava na mrežu"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dodirnite za opcije"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilna mreža nema pristup internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mreža nema pristup internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nije moguće pristupiti privatnom DNS serveru"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dodirnite da se ipak povežete"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Prebačeno na: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, uređaj koristi mrežu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata usluge."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Prebačeno iz mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> u <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mrežu"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznata vrsta mreže"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Izvori povezivosti sistema"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prijavljivanje na WiFi mrežu"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Prijava na mrežu"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Dodirnite za opcije"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilna mreža nema pristup internetu"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Mreža nema pristup internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Nije moguće pristupiti privatnom DNS serveru"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Mreža <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Dodirnite da se ipak povežete"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Prebačeno na: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, uređaj koristi mrežu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata usluge."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Prebačeno iz mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> u <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mrežu"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"prijenos podataka na mobilnoj mreži"</item>
- <item msgid="5520925862115353992">"WiFi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"prijenos podataka na mobilnoj mreži"</item>
+ <item msgid="6341719431034774569">"WiFi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"nepoznata vrsta mreže"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ca/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ca/strings.xml
index c55684d..04f6bd2 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ca/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ca/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Inicia la sessió a la xarxa Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Inicia la sessió a la xarxa"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no té accés a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toca per veure les opcions"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"La xarxa mòbil no té accés a Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"La xarxa no té accés a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"No es pot accedir al servidor DNS privat"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> té una connectivitat limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toca per connectar igualment"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Actualment en ús: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"El dispositiu utilitza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> en cas que <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tingui accés a Internet. És possible que s\'hi apliquin càrrecs."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Abans es feia servir la xarxa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>; ara s\'utilitza <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"una tipus de xarxa desconegut"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de connectivitat del sistema"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Inicia la sessió a la xarxa Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Inicia la sessió a la xarxa"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no té accés a Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toca per veure les opcions"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"La xarxa mòbil no té accés a Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"La xarxa no té accés a Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"No es pot accedir al servidor DNS privat"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> té una connectivitat limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toca per connectar igualment"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Actualment en ús: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"El dispositiu utilitza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> en cas que <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tingui accés a Internet. És possible que s\'hi apliquin càrrecs."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Abans es feia servir la xarxa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>; ara s\'utilitza <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dades mòbils"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"dades mòbils"</item>
+ <item msgid="6341719431034774569">"Wi‑Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"un tipus de xarxa desconegut"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-cs/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-cs/strings.xml
index fa8c411..6309e78 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-cs/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-cs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Přihlásit se k síti Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Přihlásit se k síti"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá přístup k internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Klepnutím zobrazíte možnosti"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilní síť nemá přístup k internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Síť nemá přístup k internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nelze získat přístup k soukromému serveru DNS"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> umožňuje jen omezené připojení"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Klepnutím se i přesto připojíte"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Přechod na síť <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Když síť <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nebude mít přístup k internetu, zařízení použije síť <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Mohou být účtovány poplatky."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Přechod ze sítě <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na síť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"neznámý typ sítě"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Zdroje pro připojení systému"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Přihlásit se k síti Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Přihlásit se k síti"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá přístup k internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Klepnutím zobrazíte možnosti"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilní síť nemá přístup k internetu"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Síť nemá přístup k internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Nelze získat přístup k soukromému serveru DNS"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Síť <xliff:g id="NETWORK_SSID">%1$s</xliff:g> umožňuje jen omezené připojení"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Klepnutím se i přesto připojíte"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Přechod na síť <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Když síť <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nebude mít přístup k internetu, zařízení použije síť <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Mohou být účtovány poplatky."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Přechod ze sítě <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na síť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilní data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobilní data"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"neznámý typ sítě"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-da/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-da/strings.xml
index f7be6df..57c58af 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-da/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-da/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Log ind på Wi-Fi-netværk"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Log ind på netværk"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetforbindelse"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tryk for at se valgmuligheder"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilnetværket har ingen internetadgang"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Netværket har ingen internetadgang"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Der er ikke adgang til den private DNS-server"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrænset forbindelse"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tryk for at oprette forbindelse alligevel"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Der blev skiftet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Enheden benytter <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, når der ikke er internetadgang via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Der opkræves muligvis betaling."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Der blev skiftet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"en ukendt netværkstype"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Systemets forbindelsesressourcer"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Log ind på Wi-Fi-netværk"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Log ind på netværk"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetforbindelse"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tryk for at se valgmuligheder"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilnetværket har ingen internetadgang"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Netværket har ingen internetadgang"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Der er ikke adgang til den private DNS-server"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrænset forbindelse"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tryk for at oprette forbindelse alligevel"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Der blev skiftet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Enheden benytter <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, når der ikke er internetadgang via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Der opkræves muligvis betaling."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Der blev skiftet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobildata"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobildata"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"en ukendt netværkstype"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-de/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-de/strings.xml
index 1e7b80c..d0c2551 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-de/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-de/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"In WLAN anmelden"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Im Netzwerk anmelden"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> hat keinen Internetzugriff"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Für Optionen tippen"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiles Netzwerk hat keinen Internetzugriff"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Netzwerk hat keinen Internetzugriff"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Schlechte Verbindung mit <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tippen, um die Verbindung trotzdem herzustellen"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Zu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> gewechselt"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Auf dem Gerät werden <xliff:g id="NEW_NETWORK">%1$s</xliff:g> genutzt, wenn über <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> kein Internet verfügbar ist. Eventuell fallen Gebühren an."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Von \"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>\" zu \"<xliff:g id="NEW_NETWORK">%2$s</xliff:g>\" gewechselt"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ein unbekannter Netzwerktyp"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Systemverbindungsressourcen"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"In WLAN anmelden"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Im Netzwerk anmelden"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> hat keinen Internetzugriff"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Für Optionen tippen"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobiles Netzwerk hat keinen Internetzugriff"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Netzwerk hat keinen Internetzugriff"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Schlechte Verbindung mit <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tippen, um die Verbindung trotzdem herzustellen"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Zu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> gewechselt"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Auf dem Gerät werden <xliff:g id="NEW_NETWORK">%1$s</xliff:g> genutzt, wenn über <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> kein Internet verfügbar ist. Eventuell fallen Gebühren an."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Von \"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>\" zu \"<xliff:g id="NEW_NETWORK">%2$s</xliff:g>\" gewechselt"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"Mobile Daten"</item>
- <item msgid="5520925862115353992">"WLAN"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"Mobile Daten"</item>
+ <item msgid="6341719431034774569">"WLAN"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"ein unbekannter Netzwerktyp"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-el/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-el/strings.xml
index 89647fdb..1c2838d 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-el/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-el/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Συνδεθείτε στο δίκτυο Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Σύνδεση στο δίκτυο"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Η εφαρμογή <xliff:g id="NETWORK_SSID">%1$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Πατήστε για να δείτε τις επιλογές"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Το δίκτυο κινητής τηλεφωνίας δεν έχει πρόσβαση στο διαδίκτυο."</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Το δίκτυο δεν έχει πρόσβαση στο διαδίκτυο."</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Το δίκτυο <xliff:g id="NETWORK_SSID">%1$s</xliff:g> έχει περιορισμένη συνδεσιμότητα"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Πατήστε για σύνδεση ούτως ή άλλως"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Μετάβαση σε δίκτυο <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Η συσκευή χρησιμοποιεί το δίκτυο <xliff:g id="NEW_NETWORK">%1$s</xliff:g> όταν το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο. Μπορεί να ισχύουν χρεώσεις."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Μετάβαση από το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> στο δίκτυο <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"άγνωστος τύπος δικτύου"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Πόροι συνδεσιμότητας συστήματος"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Συνδεθείτε στο δίκτυο Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Σύνδεση στο δίκτυο"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Η εφαρμογή <xliff:g id="NETWORK_SSID">%1$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Πατήστε για να δείτε τις επιλογές"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Το δίκτυο κινητής τηλεφωνίας δεν έχει πρόσβαση στο διαδίκτυο."</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Το δίκτυο δεν έχει πρόσβαση στο διαδίκτυο."</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Το δίκτυο <xliff:g id="NETWORK_SSID">%1$s</xliff:g> έχει περιορισμένη συνδεσιμότητα"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Πατήστε για σύνδεση ούτως ή άλλως"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Μετάβαση σε δίκτυο <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Η συσκευή χρησιμοποιεί το δίκτυο <xliff:g id="NEW_NETWORK">%1$s</xliff:g> όταν το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο. Μπορεί να ισχύουν χρεώσεις."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Μετάβαση από το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> στο δίκτυο <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"δεδομένα κινητής τηλεφωνίας"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"δεδομένα κινητής τηλεφωνίας"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"άγνωστος τύπος δικτύου"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml
index d29e2ec..db5ad70 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rAU/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System connectivity resources"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Sign in to a Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Sign in to network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tap for options"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobile network has no Internet access"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Network has no Internet access"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Private DNS server cannot be accessed"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tap to connect anyway"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobile data"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"an unknown network type"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
index d29e2ec..db5ad70 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System connectivity resources"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Sign in to a Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Sign in to network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tap for options"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobile network has no Internet access"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Network has no Internet access"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Private DNS server cannot be accessed"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tap to connect anyway"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobile data"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"an unknown network type"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml
index d29e2ec..db5ad70 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rGB/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System connectivity resources"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Sign in to a Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Sign in to network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tap for options"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobile network has no Internet access"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Network has no Internet access"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Private DNS server cannot be accessed"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tap to connect anyway"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobile data"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"an unknown network type"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml
index d29e2ec..db5ad70 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rIN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to a Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no Internet access"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no Internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System connectivity resources"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Sign in to a Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Sign in to network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no Internet access"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tap for options"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobile network has no Internet access"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Network has no Internet access"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Private DNS server cannot be accessed"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tap to connect anyway"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobile data"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"an unknown network type"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml
index cd69133..2602bfa 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-en-rXC/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Sign in to Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Sign in to network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no internet access"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tap for options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobile network has no internet access"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Network has no internet access"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Private DNS server cannot be accessed"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tap to connect anyway"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no internet access. Charges may apply."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"an unknown network type"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System Connectivity Resources"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Sign in to Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Sign in to network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has no internet access"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tap for options"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobile network has no internet access"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Network has no internet access"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Private DNS server cannot be accessed"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> has limited connectivity"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tap to connect anyway"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no internet access. Charges may apply."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobile data"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"an unknown network type"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
index 9102dc0..e5f1833 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-es-rUS/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Accede a una red Wi-Fi."</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Acceder a la red"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>no tiene acceso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Presiona para ver opciones"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"La red móvil no tiene acceso a Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"La red no tiene acceso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"No se puede acceder al servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene conectividad limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Presiona para conectarte de todas formas"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Se cambió a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"El dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Se cambió de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tipo de red desconocido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conectividad del sistema"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Accede a una red Wi-Fi."</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Acceder a la red"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>no tiene acceso a Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Presiona para ver opciones"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"La red móvil no tiene acceso a Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"La red no tiene acceso a Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"No se puede acceder al servidor DNS privado"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene conectividad limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Presiona para conectarte de todas formas"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Se cambió a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"El dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Se cambió de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"Datos móviles"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"Datos móviles"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"un tipo de red desconocido"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-es/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-es/strings.xml
index 4c15566..e4f4307 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-es/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-es/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Iniciar sesión en red Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Iniciar sesión en la red"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no tiene acceso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toca para ver opciones"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"La red móvil no tiene acceso a Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"La red no tiene acceso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"No se ha podido acceder al servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene una conectividad limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toca para conectarte de todas formas"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Se ha cambiado a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"El dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Se ha cambiado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tipo de red desconocido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conectividad del sistema"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Iniciar sesión en red Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Iniciar sesión en la red"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> no tiene acceso a Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toca para ver opciones"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"La red móvil no tiene acceso a Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"La red no tiene acceso a Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"No se ha podido acceder al servidor DNS privado"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiene una conectividad limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toca para conectarte de todas formas"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Se ha cambiado a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"El dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Se ha cambiado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"datos móviles"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"datos móviles"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"un tipo de red desconocido"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-et/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-et/strings.xml
index 398223a..cec408f 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-et/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-et/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Logi sisse WiFi-võrku"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Võrku sisselogimine"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Võrgul <xliff:g id="NETWORK_SSID">%1$s</xliff:g> puudub Interneti-ühendus"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Puudutage valikute nägemiseks"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiilsidevõrgul puudub Interneti-ühendus"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Võrgul puudub Interneti-ühendus"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Privaatsele DNS-serverile ei pääse juurde"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Võrgu <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ühendus on piiratud"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Puudutage, kui soovite siiski ühenduse luua"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Lülitati võrgule <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Seade kasutab võrku <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, kui võrgul <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> puudub juurdepääs Internetile. Rakenduda võivad tasud."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Lülitati võrgult <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> võrgule <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tundmatu võrgutüüp"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Süsteemi ühenduvuse allikad"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Logi sisse WiFi-võrku"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Võrku sisselogimine"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Võrgul <xliff:g id="NETWORK_SSID">%1$s</xliff:g> puudub Interneti-ühendus"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Puudutage valikute nägemiseks"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobiilsidevõrgul puudub Interneti-ühendus"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Võrgul puudub Interneti-ühendus"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Privaatsele DNS-serverile ei pääse juurde"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Võrgu <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ühendus on piiratud"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Puudutage, kui soovite siiski ühenduse luua"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Lülitati võrgule <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Seade kasutab võrku <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, kui võrgul <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> puudub juurdepääs Internetile. Rakenduda võivad tasud."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Lülitati võrgult <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> võrgule <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiilne andmeside"</item>
- <item msgid="5520925862115353992">"WiFi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobiilne andmeside"</item>
+ <item msgid="6341719431034774569">"WiFi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"tundmatu võrgutüüp"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-eu/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-eu/strings.xml
index dd70316..f3ee9b1 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-eu/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-eu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Hasi saioa Wi-Fi sarean"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Hasi saioa sarean"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Ezin da konektatu Internetera <xliff:g id="NETWORK_SSID">%1$s</xliff:g> sarearen bidez"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Sakatu aukerak ikusteko"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Sare mugikorra ezin da konektatu Internetera"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Sarea ezin da konektatu Internetera"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Ezin da atzitu DNS zerbitzari pribatua"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sareak konektagarritasun murriztua du"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Sakatu hala ere konektatzeko"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> erabiltzen ari zara orain"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> Internetera konektatzeko gauza ez denean, <xliff:g id="NEW_NETWORK">%1$s</xliff:g> erabiltzen du gailuak. Agian kostuak ordaindu beharko dituzu."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> erabiltzen ari zinen, baina <xliff:g id="NEW_NETWORK">%2$s</xliff:g> erabiltzen ari zara orain"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"sare mota ezezaguna"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Sistemaren konexio-baliabideak"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Hasi saioa Wi-Fi sarean"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Hasi saioa sarean"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Ezin da konektatu Internetera <xliff:g id="NETWORK_SSID">%1$s</xliff:g> sarearen bidez"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Sakatu aukerak ikusteko"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Sare mugikorra ezin da konektatu Internetera"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Sarea ezin da konektatu Internetera"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Ezin da atzitu DNS zerbitzari pribatua"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sareak konektagarritasun murriztua du"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Sakatu hala ere konektatzeko"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> erabiltzen ari zara orain"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> Internetera konektatzeko gauza ez denean, <xliff:g id="NEW_NETWORK">%1$s</xliff:g> erabiltzen du gailuak. Agian kostuak ordaindu beharko dituzu."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> erabiltzen ari zinen, baina <xliff:g id="NEW_NETWORK">%2$s</xliff:g> erabiltzen ari zara orain"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"datu-konexioa"</item>
- <item msgid="5520925862115353992">"Wifia"</item>
- <item msgid="1055487873974272842">"Bluetooth-a"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"datu-konexioa"</item>
+ <item msgid="6341719431034774569">"Wifia"</item>
+ <item msgid="5081440868800877512">"Bluetooth-a"</item>
+ <item msgid="1160736166977503463">"Ethernet-a"</item>
+ <item msgid="7347618872551558605">"VPNa"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"sare mota ezezaguna"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fa/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fa/strings.xml
index 46a946c..0c5b147 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fa/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ورود به شبکه Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ورود به سیستم شبکه"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> به اینترنت دسترسی ندارد"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"برای گزینهها ضربه بزنید"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"شبکه تلفن همراه به اینترنت دسترسی ندارد"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"شبکه به اینترنت دسترسی ندارد"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"سرور DNS خصوصی قابل دسترسی نیست"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> اتصال محدودی دارد"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"بههرصورت، برای اتصال ضربه بزنید"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"به <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> تغییر کرد"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"وقتی <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> به اینترنت دسترسی نداشته باشد، دستگاه از <xliff:g id="NEW_NETWORK">%1$s</xliff:g> استفاده میکند. ممکن است هزینههایی اعمال شود."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"از <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> به <xliff:g id="NEW_NETWORK">%2$s</xliff:g> تغییر کرد"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"نوع شبکه نامشخص"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"منابع اتصال سیستم"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ورود به شبکه Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ورود به سیستم شبکه"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> به اینترنت دسترسی ندارد"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"برای گزینهها ضربه بزنید"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"شبکه تلفن همراه به اینترنت دسترسی ندارد"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"شبکه به اینترنت دسترسی ندارد"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"سرور DNS خصوصی قابل دسترسی نیست"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> اتصال محدودی دارد"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"بههرصورت، برای اتصال ضربه بزنید"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"به <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> تغییر کرد"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"وقتی <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> به اینترنت دسترسی نداشته باشد، دستگاه از <xliff:g id="NEW_NETWORK">%1$s</xliff:g> استفاده میکند. ممکن است هزینههایی اعمال شود."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"از <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> به <xliff:g id="NEW_NETWORK">%2$s</xliff:g> تغییر کرد"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"داده تلفن همراه"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"بلوتوث"</item>
- <item msgid="1616528372438698248">"اترنت"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"داده تلفن همراه"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"بلوتوث"</item>
+ <item msgid="1160736166977503463">"اترنت"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"نوع شبکه نامشخص"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fi/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fi/strings.xml
index dd94441..84c0034 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fi/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Kirjaudu Wi-Fi-verkkoon"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Kirjaudu verkkoon"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ei ole yhteydessä internetiin"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Näytä vaihtoehdot napauttamalla."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiiliverkko ei ole yhteydessä internetiin"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Verkko ei ole yhteydessä internetiin"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> toimii rajoitetulla yhteydellä"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Yhdistä napauttamalla"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> otettiin käyttöön"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> otetaan käyttöön, kun <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ei voi muodostaa yhteyttä internetiin. Veloitukset ovat mahdollisia."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> poistettiin käytöstä ja <xliff:g id="NEW_NETWORK">%2$s</xliff:g> otettiin käyttöön."</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tuntematon verkon tyyppi"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Järjestelmän yhteysresurssit"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Kirjaudu Wi-Fi-verkkoon"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Kirjaudu verkkoon"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ei ole yhteydessä internetiin"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Näytä vaihtoehdot napauttamalla."</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobiiliverkko ei ole yhteydessä internetiin"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Verkko ei ole yhteydessä internetiin"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> toimii rajoitetulla yhteydellä"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Yhdistä napauttamalla"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> otettiin käyttöön"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> otetaan käyttöön, kun <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ei voi muodostaa yhteyttä internetiin. Veloitukset ovat mahdollisia."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> poistettiin käytöstä ja <xliff:g id="NEW_NETWORK">%2$s</xliff:g> otettiin käyttöön."</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiilidata"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobiilidata"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"tuntematon verkon tyyppi"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml
index 02ef50b..0badf1b 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Connectez-vous au réseau Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Connectez-vous au réseau"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> n\'offre aucun accès à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Touchez pour afficher les options"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Le réseau cellulaire n\'offre aucun accès à Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Le réseau n\'offre aucun accès à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Impossible d\'accéder au serveur DNS privé"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> offre une connectivité limitée"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Touchez pour vous connecter quand même"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Passé au réseau <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quand <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas d\'accès à Internet. Des frais peuvent s\'appliquer."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Passé du réseau <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> au <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un type de réseau inconnu"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ressources de connectivité système"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Connectez-vous au réseau Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Connectez-vous au réseau"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> n\'offre aucun accès à Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Touchez pour afficher les options"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Le réseau cellulaire n\'offre aucun accès à Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Le réseau n\'offre aucun accès à Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Impossible d\'accéder au serveur DNS privé"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Le réseau <xliff:g id="NETWORK_SSID">%1$s</xliff:g> offre une connectivité limitée"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Touchez pour vous connecter quand même"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Passé au réseau <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quand <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas d\'accès à Internet. Des frais peuvent s\'appliquer."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Passé du réseau <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> au <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"données cellulaires"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"RPV"</item>
+ <item msgid="5454013645032700715">"données cellulaires"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"RPV"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"un type de réseau inconnu"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr/strings.xml
index 08c9d81..b483525 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-fr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Connectez-vous au réseau Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Se connecter au réseau"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Aucune connexion à Internet pour <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Appuyez ici pour afficher des options."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Le réseau mobile ne dispose d\'aucun accès à Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Le réseau ne dispose d\'aucun accès à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Impossible d\'accéder au serveur DNS privé"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"La connectivité de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> est limitée"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Appuyer pour se connecter quand même"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Nouveau réseau : <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> lorsque <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas de connexion Internet. Des frais peuvent s\'appliquer."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Ancien réseau : <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>. Nouveau réseau : <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"type de réseau inconnu"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ressources de connectivité système"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Connectez-vous au réseau Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Se connecter au réseau"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Aucune connexion à Internet pour <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Appuyez ici pour afficher des options."</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Le réseau mobile ne dispose d\'aucun accès à Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Le réseau ne dispose d\'aucun accès à Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Impossible d\'accéder au serveur DNS privé"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"La connectivité de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> est limitée"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Appuyer pour se connecter quand même"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Nouveau réseau : <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> lorsque <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas de connexion Internet. Des frais peuvent s\'appliquer."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Ancien réseau : <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>. Nouveau réseau : <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"données mobiles"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"données mobiles"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"type de réseau inconnu"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-gl/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-gl/strings.xml
index 9f98055..dfe8137 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-gl/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-gl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Inicia sesión na rede wifi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Inicia sesión na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ten acceso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toca para ver opcións."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"A rede de telefonía móbil non ten acceso a Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede non ten acceso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Non se puido acceder ao servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"A conectividade de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> é limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toca para conectarte de todas formas"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Cambiouse a: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ten acceso a Internet. Pódense aplicar cargos."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Cambiouse de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tipo de rede descoñecido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conectividade do sistema"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Inicia sesión na rede wifi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Inicia sesión na rede"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ten acceso a Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toca para ver opcións."</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"A rede de telefonía móbil non ten acceso a Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"A rede non ten acceso a Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Non se puido acceder ao servidor DNS privado"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"A conectividade de <xliff:g id="NETWORK_SSID">%1$s</xliff:g> é limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toca para conectarte de todas formas"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Cambiouse a: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ten acceso a Internet. Pódense aplicar cargos."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Cambiouse de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"datos móbiles"</item>
- <item msgid="5520925862115353992">"wifi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"datos móbiles"</item>
+ <item msgid="6341719431034774569">"wifi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"un tipo de rede descoñecido"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-gu/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-gu/strings.xml
index 4ae5a2c..e49b11d 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-gu/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-gu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"વાઇ-ફાઇ નેટવર્ક પર સાઇન ઇન કરો"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"નેટવર્ક પર સાઇન ઇન કરો"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"વિકલ્પો માટે ટૅપ કરો"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"મોબાઇલ નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> મર્યાદિત કનેક્ટિવિટી ધરાવે છે"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"છતાં કનેક્ટ કરવા માટે ટૅપ કરો"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> પર સ્વિચ કર્યું"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"જ્યારે <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> પાસે કોઈ ઇન્ટરનેટ ઍક્સેસ ન હોય ત્યારે ઉપકરણ <xliff:g id="NEW_NETWORK">%1$s</xliff:g>નો ઉપયોગ કરે છે. શુલ્ક લાગુ થઈ શકે છે."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> પરથી <xliff:g id="NEW_NETWORK">%2$s</xliff:g> પર સ્વિચ કર્યું"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"અજાણ્યો નેટવર્ક પ્રકાર"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"સિસ્ટમની કનેક્ટિવિટીનાં સાધનો"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"વાઇ-ફાઇ નેટવર્ક પર સાઇન ઇન કરો"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"નેટવર્ક પર સાઇન ઇન કરો"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"વિકલ્પો માટે ટૅપ કરો"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"મોબાઇલ નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"નેટવર્ક કોઈ ઇન્ટરનેટ ઍક્સેસ ધરાવતું નથી"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> મર્યાદિત કનેક્ટિવિટી ધરાવે છે"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"છતાં કનેક્ટ કરવા માટે ટૅપ કરો"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> પર સ્વિચ કર્યું"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"જ્યારે <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> પાસે કોઈ ઇન્ટરનેટ ઍક્સેસ ન હોય ત્યારે ઉપકરણ <xliff:g id="NEW_NETWORK">%1$s</xliff:g>નો ઉપયોગ કરે છે. શુલ્ક લાગુ થઈ શકે છે."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> પરથી <xliff:g id="NEW_NETWORK">%2$s</xliff:g> પર સ્વિચ કર્યું"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"મોબાઇલ ડેટા"</item>
- <item msgid="5520925862115353992">"વાઇ-ફાઇ"</item>
- <item msgid="1055487873974272842">"બ્લૂટૂથ"</item>
- <item msgid="1616528372438698248">"ઇથરનેટ"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"મોબાઇલ ડેટા"</item>
+ <item msgid="6341719431034774569">"વાઇ-ફાઇ"</item>
+ <item msgid="5081440868800877512">"બ્લૂટૂથ"</item>
+ <item msgid="1160736166977503463">"ઇથરનેટ"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"કોઈ અજાણ્યો નેટવર્કનો પ્રકાર"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hi/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hi/strings.xml
index eff1b60..80ed699 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hi/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"वाई-फ़ाई नेटवर्क में साइन इन करें"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"नेटवर्क में साइन इन करें"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> का इंटरनेट नहीं चल रहा है"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"विकल्पों के लिए टैप करें"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"मोबाइल नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"इस नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> की कनेक्टिविटी सीमित है"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"फिर भी कनेक्ट करने के लिए टैप करें"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> पर ले जाया गया"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> में इंटरनेट की सुविधा नहीं होने पर डिवाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> का इस्तेमाल करता है. इसके लिए शुल्क लिया जा सकता है."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> से <xliff:g id="NEW_NETWORK">%2$s</xliff:g> पर ले जाया गया"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"अज्ञात नेटवर्क प्रकार"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"सिस्टम कनेक्टिविटी के संसाधन"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"वाई-फ़ाई नेटवर्क में साइन इन करें"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"नेटवर्क में साइन इन करें"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> का इंटरनेट नहीं चल रहा है"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"विकल्पों के लिए टैप करें"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"मोबाइल नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"इस नेटवर्क पर इंटरनेट ऐक्सेस नहीं है"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> की कनेक्टिविटी सीमित है"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"फिर भी कनेक्ट करने के लिए टैप करें"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> पर ले जाया गया"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> में इंटरनेट की सुविधा नहीं होने पर डिवाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> का इस्तेमाल करता है. इसके लिए शुल्क लिया जा सकता है."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> से <xliff:g id="NEW_NETWORK">%2$s</xliff:g> पर ले जाया गया"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"मोबाइल डेटा"</item>
- <item msgid="5520925862115353992">"वाई-फ़ाई"</item>
- <item msgid="1055487873974272842">"ब्लूटूथ"</item>
- <item msgid="1616528372438698248">"ईथरनेट"</item>
- <item msgid="9177085807664964627">"वीपीएन"</item>
+ <item msgid="5454013645032700715">"मोबाइल डेटा"</item>
+ <item msgid="6341719431034774569">"वाई-फ़ाई"</item>
+ <item msgid="5081440868800877512">"ब्लूटूथ"</item>
+ <item msgid="1160736166977503463">"ईथरनेट"</item>
+ <item msgid="7347618872551558605">"वीपीएन"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"अज्ञात नेटवर्क टाइप"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hr/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hr/strings.xml
index 52bfb93..24bb22f 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hr/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijava na Wi-Fi mrežu"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prijava na mrežu"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dodirnite za opcije"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilna mreža nema pristup internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mreža nema pristup internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dodirnite da biste se ipak povezali"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Prelazak na drugu mrežu: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, na uređaju se upotrebljava <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata naknade."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Mreža je promijenjena: <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> > <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznata vrsta mreže"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resursi za povezivost sustava"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prijava na Wi-Fi mrežu"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Prijava na mrežu"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nema pristup internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Dodirnite za opcije"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilna mreža nema pristup internetu"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Mreža nema pristup internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ima ograničenu povezivost"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Dodirnite da biste se ipak povezali"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Prelazak na drugu mrežu: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, na uređaju se upotrebljava <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata naknade."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Mreža je promijenjena: <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> > <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilni podaci"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobilni podaci"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"nepoznata vrsta mreže"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hu/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hu/strings.xml
index 2ff59b5..47a1142 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hu/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Bejelentkezés Wi-Fi hálózatba"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Bejelentkezés a hálózatba"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózaton nincs internet-hozzáférés"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Koppintson a beállítások megjelenítéséhez"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"A mobilhálózaton nincs internet-hozzáférés"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"A hálózaton nincs internet-hozzáférés"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózat korlátozott kapcsolatot biztosít"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Koppintson, ha mindenképpen csatlakozni szeretne"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Átváltva erre: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> használata, ha nincs internet-hozzáférés <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-kapcsolaton keresztül. A szolgáltató díjat számíthat fel."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Átváltva <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-hálózatról erre: <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ismeretlen hálózati típus"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Rendszerkapcsolat erőforrásai"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Bejelentkezés Wi-Fi hálózatba"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Bejelentkezés a hálózatba"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózaton nincs internet-hozzáférés"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Koppintson a beállítások megjelenítéséhez"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"A mobilhálózaton nincs internet-hozzáférés"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"A hálózaton nincs internet-hozzáférés"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"A(z) <xliff:g id="NETWORK_SSID">%1$s</xliff:g> hálózat korlátozott kapcsolatot biztosít"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Koppintson, ha mindenképpen csatlakozni szeretne"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Átváltva erre: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> használata, ha nincs internet-hozzáférés <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-kapcsolaton keresztül. A szolgáltató díjat számíthat fel."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Átváltva <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-hálózatról erre: <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiladatok"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobiladatok"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"ismeretlen hálózati típus"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hy/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hy/strings.xml
index b35d31c..dd951e8 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-hy/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-hy/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Մուտք գործեք Wi-Fi ցանց"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Մուտք գործեք ցանց"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցը չունի մուտք ինտերնետին"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Հպեք՝ ընտրանքները տեսնելու համար"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Բջջային ցանցը չի ապահովում ինտերնետ կապ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Ցանցը միացված չէ ինտերնետին"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Մասնավոր DNS սերվերն անհասանելի է"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցի կապը սահմանափակ է"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Հպեք՝ միանալու համար"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Անցել է <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ցանցի"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Երբ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ցանցում ինտերնետ կապ չի լինում, սարքն անցնում է <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ցանցի: Նման դեպքում կարող են վճարներ գանձվել:"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ցանցից անցել է <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ցանցի"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ցանցի անհայտ տեսակ"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System Connectivity Resources"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Մուտք գործեք Wi-Fi ցանց"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Մուտք գործեք ցանց"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցը չունի մուտք ինտերնետին"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Հպեք՝ ընտրանքները տեսնելու համար"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Բջջային ցանցը չի ապահովում ինտերնետ կապ"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Ցանցը միացված չէ ինտերնետին"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Մասնավոր DNS սերվերն անհասանելի է"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ցանցի կապը սահմանափակ է"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Հպեք՝ միանալու համար"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Անցել է <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ցանցի"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Երբ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ցանցում ինտերնետ կապ չի լինում, սարքն անցնում է <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ցանցի: Նման դեպքում կարող են վճարներ գանձվել:"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ցանցից անցել է <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ցանցի"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"բջջային ինտերնետ"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"բջջային ինտերնետ"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"ցանցի անհայտ տեսակ"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-in/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-in/strings.xml
index 27d7d89..d559f6b 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-in/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-in/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Login ke jaringan Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Login ke jaringan"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tidak memiliki akses internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Ketuk untuk melihat opsi"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Jaringan seluler tidak memiliki akses internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Jaringan tidak memiliki akses internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Server DNS pribadi tidak dapat diakses"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> memiliki konektivitas terbatas"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ketuk untuk tetap menyambungkan"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Dialihkan ke <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Perangkat menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> jika <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tidak memiliki akses internet. Tarif mungkin berlaku."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Dialihkan dari <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ke <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"jenis jaringan yang tidak dikenal"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resource Konektivitas Sistem"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Login ke jaringan Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Login ke jaringan"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tidak memiliki akses internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Ketuk untuk melihat opsi"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Jaringan seluler tidak memiliki akses internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Jaringan tidak memiliki akses internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Server DNS pribadi tidak dapat diakses"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> memiliki konektivitas terbatas"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Ketuk untuk tetap menyambungkan"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Dialihkan ke <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Perangkat menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> jika <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tidak memiliki akses internet. Tarif mungkin berlaku."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Dialihkan dari <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ke <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"data seluler"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"data seluler"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"jenis jaringan yang tidak dikenal"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-is/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-is/strings.xml
index 97f42dc..877c85f 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-is/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-is/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Skrá inn á Wi-Fi net"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Skrá inn á net"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> er ekki með internetaðgang"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Ýttu til að sjá valkosti"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Farsímakerfið er ekki tengt við internetið"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Netkerfið er ekki tengt við internetið"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Ekki næst í DNS-einkaþjón"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Tengigeta <xliff:g id="NETWORK_SSID">%1$s</xliff:g> er takmörkuð"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ýttu til að tengjast samt"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Skipt yfir á <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Tækið notar <xliff:g id="NEW_NETWORK">%1$s</xliff:g> þegar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> er ekki með internetaðgang. Gjöld kunna að eiga við."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Skipt úr <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> yfir í <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"óþekkt tegund netkerfis"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Tengigögn kerfis"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Skrá inn á Wi-Fi net"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Skrá inn á net"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> er ekki með internetaðgang"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Ýttu til að sjá valkosti"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Farsímakerfið er ekki tengt við internetið"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Netkerfið er ekki tengt við internetið"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Ekki næst í DNS-einkaþjón"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Tengigeta <xliff:g id="NETWORK_SSID">%1$s</xliff:g> er takmörkuð"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Ýttu til að tengjast samt"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Skipt yfir á <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Tækið notar <xliff:g id="NEW_NETWORK">%1$s</xliff:g> þegar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> er ekki með internetaðgang. Gjöld kunna að eiga við."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Skipt úr <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> yfir í <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"farsímagögn"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"farsímagögn"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"óþekkt tegund netkerfis"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-it/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-it/strings.xml
index 7836101..bcac393 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-it/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-it/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Accedi a rete Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Accedi alla rete"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ha accesso a Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tocca per le opzioni"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"La rete mobile non ha accesso a Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"La rete non ha accesso a Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Non è possibile accedere al server DNS privato"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ha una connettività limitata"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tocca per connettere comunque"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Passato a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Il dispositivo utilizza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ha accesso a Internet. Potrebbero essere applicati costi."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Passato da <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"tipo di rete sconosciuto"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Risorse per connettività di sistema"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Accedi a rete Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Accedi alla rete"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> non ha accesso a Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tocca per le opzioni"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"La rete mobile non ha accesso a Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"La rete non ha accesso a Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Non è possibile accedere al server DNS privato"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ha una connettività limitata"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tocca per connettere comunque"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Passato a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Il dispositivo utilizza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ha accesso a Internet. Potrebbero essere applicati costi."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Passato da <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dati mobili"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"dati mobili"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"tipo di rete sconosciuto"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-iw/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-iw/strings.xml
index f322b99..d6684ce 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-iw/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-iw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"היכנס לרשת Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"היכנס לרשת"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"ל-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> אין גישה לאינטרנט"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"הקש לקבלת אפשרויות"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"לרשת הסלולרית אין גישה לאינטרנט"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"לרשת אין גישה לאינטרנט"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"לא ניתן לגשת לשרת DNS הפרטי"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"הקישוריות של <xliff:g id="NETWORK_SSID">%1$s</xliff:g> מוגבלת"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"כדי להתחבר למרות זאת יש להקיש"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"מעבר אל <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"המכשיר משתמש ברשת <xliff:g id="NEW_NETWORK">%1$s</xliff:g> כשלרשת <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> אין גישה לאינטרנט. עשויים לחול חיובים."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"עבר מרשת <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> לרשת <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"סוג רשת לא מזוהה"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"משאבי קישוריות מערכת"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"היכנס לרשת Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"היכנס לרשת"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"ל-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> אין גישה לאינטרנט"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"הקש לקבלת אפשרויות"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"לרשת הסלולרית אין גישה לאינטרנט"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"לרשת אין גישה לאינטרנט"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"לא ניתן לגשת לשרת DNS הפרטי"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"הקישוריות של <xliff:g id="NETWORK_SSID">%1$s</xliff:g> מוגבלת"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"כדי להתחבר למרות זאת יש להקיש"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"מעבר אל <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"המכשיר משתמש ברשת <xliff:g id="NEW_NETWORK">%1$s</xliff:g> כשלרשת <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> אין גישה לאינטרנט. עשויים לחול חיובים."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"עבר מרשת <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> לרשת <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"חבילת גלישה"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"חבילת גלישה"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"אתרנט"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"סוג רשת לא מזוהה"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ja/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ja/strings.xml
index 1aa3216..fa4a30a 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ja/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ja/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fiネットワークにログイン"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ネットワークにログインしてください"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> はインターネットにアクセスできません"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"タップしてその他のオプションを表示"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"モバイル ネットワークがインターネットに接続されていません"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ネットワークがインターネットに接続されていません"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"プライベート DNS サーバーにアクセスできません"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> の接続が制限されています"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"接続するにはタップしてください"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"「<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>」に切り替えました"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"デバイスで「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」によるインターネット接続ができない場合に「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」を使用します。通信料が発生することがあります。"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"「<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>」から「<xliff:g id="NEW_NETWORK">%2$s</xliff:g>」に切り替えました"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"不明なネットワーク タイプ"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"システム接続リソース"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fiネットワークにログイン"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ネットワークにログインしてください"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> はインターネットにアクセスできません"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"タップしてその他のオプションを表示"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"モバイル ネットワークがインターネットに接続されていません"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"ネットワークがインターネットに接続されていません"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"プライベート DNS サーバーにアクセスできません"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> の接続が制限されています"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"接続するにはタップしてください"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"「<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>」に切り替えました"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"デバイスで「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」によるインターネット接続ができない場合に「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」を使用します。通信料が発生することがあります。"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"「<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>」から「<xliff:g id="NEW_NETWORK">%2$s</xliff:g>」に切り替えました"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"モバイルデータ"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"イーサネット"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"モバイルデータ"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"イーサネット"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"不明なネットワーク タイプ"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ka/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ka/strings.xml
index 61d21b5..4183310 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ka/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ka/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi ქსელთან დაკავშირება"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ქსელში შესვლა"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"შეეხეთ ვარიანტების სანახავად"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"მობილურ ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ის კავშირები შეზღუდულია"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"შეეხეთ, თუ მაინც გსურთ დაკავშირება"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"ახლა გამოიყენება <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"თუ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ინტერნეტთან კავშირს დაკარგავს, მოწყობილობის მიერ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> იქნება გამოყენებული, რამაც შეიძლება დამატებითი ხარჯები გამოიწვიოს."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"ახლა გამოიყენება <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> (გამოიყენებოდა <xliff:g id="NEW_NETWORK">%2$s</xliff:g>)"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"უცნობი ტიპის ქსელი"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"სისტემის კავშირის რესურსები"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi ქსელთან დაკავშირება"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ქსელში შესვლა"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ს არ აქვს ინტერნეტზე წვდომა"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"შეეხეთ ვარიანტების სანახავად"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"მობილურ ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-ის კავშირები შეზღუდულია"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"შეეხეთ, თუ მაინც გსურთ დაკავშირება"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"ახლა გამოიყენება <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"თუ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ინტერნეტთან კავშირს დაკარგავს, მოწყობილობის მიერ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> იქნება გამოყენებული, რამაც შეიძლება დამატებითი ხარჯები გამოიწვიოს."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"ახლა გამოიყენება <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> (გამოიყენებოდა <xliff:g id="NEW_NETWORK">%2$s</xliff:g>)"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"მობილური ინტერნეტი"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"მობილური ინტერნეტი"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"უცნობი ტიპის ქსელი"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-kk/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-kk/strings.xml
index 969aaef..54d5eb3 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-kk/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-kk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi желісіне кіру"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Желіге кіру"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің интернетті пайдалану мүмкіндігі шектеулі."</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Опциялар үшін түртіңіз"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобильдік желі интернетке қосылмаған."</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Желі интернетке қосылмаған."</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Жеке DNS серверіне кіру мүмкін емес."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің қосылу мүмкіндігі шектеулі."</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Бәрібір жалғау үшін түртіңіз."</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> желісіне ауысты"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Құрылғы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> желісінде интернетпен байланыс жоғалған жағдайда <xliff:g id="NEW_NETWORK">%1$s</xliff:g> желісін пайдаланады. Деректер ақысы алынуы мүмкін."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> желісінен <xliff:g id="NEW_NETWORK">%2$s</xliff:g> желісіне ауысты"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"желі түрі белгісіз"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Жүйе байланысы ресурстары"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi желісіне кіру"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Желіге кіру"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің интернетті пайдалану мүмкіндігі шектеулі."</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Опциялар үшін түртіңіз"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Мобильдік желі интернетке қосылмаған."</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Желі интернетке қосылмаған."</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Жеке DNS серверіне кіру мүмкін емес."</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> желісінің қосылу мүмкіндігі шектеулі."</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Бәрібір жалғау үшін түртіңіз."</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> желісіне ауысты"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Құрылғы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> желісінде интернетпен байланыс жоғалған жағдайда <xliff:g id="NEW_NETWORK">%1$s</xliff:g> желісін пайдаланады. Деректер ақысы алынуы мүмкін."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> желісінен <xliff:g id="NEW_NETWORK">%2$s</xliff:g> желісіне ауысты"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобильдік деректер"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"мобильдік деректер"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"желі түрі белгісіз"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-km/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-km/strings.xml
index da3c337..bd778a1 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-km/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-km/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ចូលបណ្ដាញវ៉ាយហ្វាយ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ចូលទៅបណ្តាញ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ប៉ះសម្រាប់ជម្រើស"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"បណ្ដាញទូរសព្ទចល័តមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"បណ្ដាញមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"មិនអាចចូលប្រើម៉ាស៊ីនមេ DNS ឯកជនបានទេ"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មានការតភ្ជាប់មានកម្រិត"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"មិនអីទេ ចុចភ្ជាប់ចុះ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"បានប្តូរទៅ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"ឧបករណ៍ប្រើ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> នៅពេលដែល <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិត។ អាចគិតថ្លៃលើការប្រើប្រាស់ទិន្នន័យ។"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"បានប្តូរពី <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ទៅ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ប្រភេទបណ្តាញមិនស្គាល់"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ធនធានតភ្ជាប់ប្រព័ន្ធ"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ចូលបណ្ដាញវ៉ាយហ្វាយ"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ចូលទៅបណ្តាញ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ប៉ះសម្រាប់ជម្រើស"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"បណ្ដាញទូរសព្ទចល័តមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"បណ្ដាញមិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"មិនអាចចូលប្រើម៉ាស៊ីនមេ DNS ឯកជនបានទេ"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> មានការតភ្ជាប់មានកម្រិត"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"មិនអីទេ ចុចភ្ជាប់ចុះ"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"បានប្តូរទៅ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"ឧបករណ៍ប្រើ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> នៅពេលដែល <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិត។ អាចគិតថ្លៃលើការប្រើប្រាស់ទិន្នន័យ។"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"បានប្តូរពី <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ទៅ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ទិន្នន័យទូរសព្ទចល័ត"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"ប៊្លូធូស"</item>
- <item msgid="1616528372438698248">"អ៊ីសឺរណិត"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"ទិន្នន័យទូរសព្ទចល័ត"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"ប៊្លូធូស"</item>
+ <item msgid="1160736166977503463">"អ៊ីសឺរណិត"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"ប្រភេទបណ្តាញដែលមិនស្គាល់"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-kn/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-kn/strings.xml
index 3b5e047..7f3a420 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-kn/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-kn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ಮೊಬೈಲ್ ನೆಟ್ವರ್ಕ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ನೆಟ್ವರ್ಕ್ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ಖಾಸಗಿ DNS ಸರ್ವರ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಸೀಮಿತ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿದೆ"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ಹೇಗಾದರೂ ಸಂಪರ್ಕಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ ಹೊಂದಿಲ್ಲದಿರುವಾಗ, ಸಾಧನವು <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ಬಳಸುತ್ತದೆ. ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ರಿಂದ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ಅಪರಿಚಿತ ನೆಟ್ವರ್ಕ್ ಪ್ರಕಾರ"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ಸಿಸ್ಟಂ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆ ಮಾಹಿತಿಯ ಮೂಲಗಳು"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"ಮೊಬೈಲ್ ನೆಟ್ವರ್ಕ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"ನೆಟ್ವರ್ಕ್ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ಖಾಸಗಿ DNS ಸರ್ವರ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ಸೀಮಿತ ಸಂಪರ್ಕ ಕಲ್ಪಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿದೆ"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ಹೇಗಾದರೂ ಸಂಪರ್ಕಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ ಹೊಂದಿಲ್ಲದಿರುವಾಗ, ಸಾಧನವು <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ಬಳಸುತ್ತದೆ. ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ರಿಂದ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ಮೊಬೈಲ್ ಡೇಟಾ"</item>
- <item msgid="5520925862115353992">"ವೈ-ಫೈ"</item>
- <item msgid="1055487873974272842">"ಬ್ಲೂಟೂತ್"</item>
- <item msgid="1616528372438698248">"ಇಥರ್ನೆಟ್"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"ಮೊಬೈಲ್ ಡೇಟಾ"</item>
+ <item msgid="6341719431034774569">"ವೈ-ಫೈ"</item>
+ <item msgid="5081440868800877512">"ಬ್ಲೂಟೂತ್"</item>
+ <item msgid="1160736166977503463">"ಇಥರ್ನೆಟ್"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"ಅಪರಿಚಿತ ನೆಟ್ವರ್ಕ್ ಪ್ರಕಾರ"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ko/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ko/strings.xml
index 874bd75..a763cc5 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ko/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ko/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi 네트워크에 로그인"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"네트워크에 로그인"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>이(가) 인터넷에 액세스할 수 없습니다."</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"탭하여 옵션 보기"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"모바일 네트워크에 인터넷이 연결되어 있지 않습니다."</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"네트워크에 인터넷이 연결되어 있지 않습니다."</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>에서 연결을 제한했습니다."</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"계속 연결하려면 탭하세요."</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>(으)로 전환"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>(으)로 인터넷에 연결할 수 없는 경우 기기에서 <xliff:g id="NEW_NETWORK">%1$s</xliff:g>이(가) 사용됩니다. 요금이 부과될 수 있습니다."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>에서 <xliff:g id="NEW_NETWORK">%2$s</xliff:g>(으)로 전환"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"알 수 없는 네트워크 유형"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"시스템 연결 리소스"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi 네트워크에 로그인"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"네트워크에 로그인"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>이(가) 인터넷에 액세스할 수 없습니다."</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"탭하여 옵션 보기"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"모바일 네트워크에 인터넷이 연결되어 있지 않습니다."</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"네트워크에 인터넷이 연결되어 있지 않습니다."</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>에서 연결을 제한했습니다."</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"계속 연결하려면 탭하세요."</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>(으)로 전환"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>(으)로 인터넷에 연결할 수 없는 경우 기기에서 <xliff:g id="NEW_NETWORK">%1$s</xliff:g>이(가) 사용됩니다. 요금이 부과될 수 있습니다."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>에서 <xliff:g id="NEW_NETWORK">%2$s</xliff:g>(으)로 전환"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"모바일 데이터"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"블루투스"</item>
- <item msgid="1616528372438698248">"이더넷"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"모바일 데이터"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"블루투스"</item>
+ <item msgid="1160736166977503463">"이더넷"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"알 수 없는 네트워크 유형"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ky/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ky/strings.xml
index 1ace4dc..3550af8 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ky/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ky/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi түйүнүнө кирүү"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Тармакка кирүү"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> Интернетке туташуусу жок"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Параметрлерди ачуу үчүн таптап коюңуз"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилдик Интернет жок"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Тармактын Интернет жок"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Жеке DNS сервери жеткиликсиз"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> байланышы чектелген"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Баары бир туташуу үчүн таптаңыз"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> тармагына которуштурулду"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> тармагы Интернетке туташпай турганда, түзмөгүңүз <xliff:g id="NEW_NETWORK">%1$s</xliff:g> тармагын колдонот. Акы алынышы мүмкүн."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> дегенден <xliff:g id="NEW_NETWORK">%2$s</xliff:g> тармагына которуштурулду"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"белгисиз тармак түрү"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Тутумдун байланыш булагы"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi түйүнүнө кирүү"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Тармакка кирүү"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> Интернетке туташуусу жок"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Параметрлерди ачуу үчүн таптап коюңуз"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Мобилдик Интернет жок"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Тармактын Интернет жок"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Жеке DNS сервери жеткиликсиз"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> байланышы чектелген"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Баары бир туташуу үчүн таптаңыз"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> тармагына которуштурулду"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> тармагы Интернетке туташпай турганда, түзмөгүңүз <xliff:g id="NEW_NETWORK">%1$s</xliff:g> тармагын колдонот. Акы алынышы мүмкүн."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> дегенден <xliff:g id="NEW_NETWORK">%2$s</xliff:g> тармагына которуштурулду"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобилдик трафик"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"мобилдик трафик"</item>
+ <item msgid="6341719431034774569">"Wi‑Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"белгисиз тармак түрү"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-lo/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lo/strings.xml
index 3db497e..4b3056f 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-lo/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lo/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ເຂົ້າສູ່ລະບົບເຄືອຂ່າຍ Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ລົງຊື່ເຂົ້າເຄືອຂ່າຍ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ແຕະເພື່ອເບິ່ງຕົວເລືອກ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ເຄືອຂ່າຍມືຖືບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ເຄືອຂ່າຍບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ມີການເຊື່ອມຕໍ່ທີ່ຈຳກັດ"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ແຕະເພື່ອຢືນຢັນການເຊື່ອມຕໍ່"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"ສະຫຼັບໄປໃຊ້ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ແລ້ວ"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"ອຸປະກອນຈະໃຊ້ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ເມື່ອ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ. ອາດມີການຮຽກເກັບຄ່າບໍລິການ."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"ສະຫຼັບຈາກ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ໄປໃຊ້ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ແລ້ວ"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ບໍ່ຮູ້ຈັກປະເພດເຄືອຂ່າຍ"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ແຫຼ່ງຂໍ້ມູນການເຊື່ອມຕໍ່ລະບົບ"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ເຂົ້າສູ່ລະບົບເຄືອຂ່າຍ Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ລົງຊື່ເຂົ້າເຄືອຂ່າຍ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ແຕະເພື່ອເບິ່ງຕົວເລືອກ"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"ເຄືອຂ່າຍມືຖືບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"ເຄືອຂ່າຍບໍ່ສາມາດເຂົ້າເຖິງອິນເຕີເນັດໄດ້"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ມີການເຊື່ອມຕໍ່ທີ່ຈຳກັດ"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ແຕະເພື່ອຢືນຢັນການເຊື່ອມຕໍ່"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"ສະຫຼັບໄປໃຊ້ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ແລ້ວ"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"ອຸປະກອນຈະໃຊ້ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ເມື່ອ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ. ອາດມີການຮຽກເກັບຄ່າບໍລິການ."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"ສະຫຼັບຈາກ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ໄປໃຊ້ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ແລ້ວ"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ອິນເຕີເນັດມືຖື"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"ອີເທີເນັດ"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"ອິນເຕີເນັດມືຖື"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"ອີເທີເນັດ"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"ບໍ່ຮູ້ຈັກປະເພດເຄືອຂ່າຍ"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-lt/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lt/strings.xml
index c639c1f..8eb41f1 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-lt/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prisijungti prie „Wi-Fi“ tinklo"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prisijungti prie tinklo"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ negali pasiekti interneto"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Palieskite, kad būtų rodomos parinktys."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiliojo ryšio tinkle nėra prieigos prie interneto"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Tinkle nėra prieigos prie interneto"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Privataus DNS serverio negalima pasiekti"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ ryšys apribotas"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Palieskite, jei vis tiek norite prisijungti"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Perjungta į tinklą <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Įrenginyje naudojamas kitas tinklas (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>), kai dabartiniame tinkle (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nėra interneto ryšio. Gali būti taikomi mokesčiai."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Perjungta iš tinklo <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> į tinklą <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nežinomas tinklo tipas"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System Connectivity Resources"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prisijungti prie „Wi-Fi“ tinklo"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Prisijungti prie tinklo"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ negali pasiekti interneto"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Palieskite, kad būtų rodomos parinktys."</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobiliojo ryšio tinkle nėra prieigos prie interneto"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Tinkle nėra prieigos prie interneto"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Privataus DNS serverio negalima pasiekti"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"„<xliff:g id="NETWORK_SSID">%1$s</xliff:g>“ ryšys apribotas"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Palieskite, jei vis tiek norite prisijungti"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Perjungta į tinklą <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Įrenginyje naudojamas kitas tinklas (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>), kai dabartiniame tinkle (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nėra interneto ryšio. Gali būti taikomi mokesčiai."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Perjungta iš tinklo <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> į tinklą <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiliojo ryšio duomenys"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Eternetas"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobiliojo ryšio duomenys"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Eternetas"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"nežinomas tinklo tipas"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-lv/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lv/strings.xml
index 5774603..0647a4f 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-lv/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-lv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Pierakstieties Wi-Fi tīklā"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Pierakstīšanās tīklā"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nav piekļuves internetam"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Pieskarieties, lai skatītu iespējas."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilajā tīklā nav piekļuves internetam."</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Tīklā nav piekļuves internetam."</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Nevar piekļūt privātam DNS serverim."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ir ierobežota savienojamība"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Lai tik un tā izveidotu savienojumu, pieskarieties"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Pārslēdzās uz tīklu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kad vienā tīklā (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nav piekļuves internetam, ierīcē tiek izmantots cits tīkls (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>). Var tikt piemērota maksa."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Pārslēdzās no tīkla <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> uz tīklu <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nezināms tīkla veids"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Sistēmas savienojamības resursi"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Pierakstieties Wi-Fi tīklā"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Pierakstīšanās tīklā"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nav piekļuves internetam"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Pieskarieties, lai skatītu iespējas."</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilajā tīklā nav piekļuves internetam."</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Tīklā nav piekļuves internetam."</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Nevar piekļūt privātam DNS serverim."</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Tīklā <xliff:g id="NETWORK_SSID">%1$s</xliff:g> ir ierobežota savienojamība"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Lai tik un tā izveidotu savienojumu, pieskarieties"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Pārslēdzās uz tīklu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Kad vienā tīklā (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nav piekļuves internetam, ierīcē tiek izmantots cits tīkls (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>). Var tikt piemērota maksa."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Pārslēdzās no tīkla <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> uz tīklu <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilie dati"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobilie dati"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"nezināms tīkla veids"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mk/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mk/strings.xml
index 053c7cf..b0024e2 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mk/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Најавете се на мрежа на Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Најавете се на мрежа"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема интернет-пристап"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Допрете за опции"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилната мрежа нема интернет-пристап"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Мрежата нема интернет-пристап"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Не може да се пристапи до приватниот DNS-сервер"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена поврзливост"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Допрете за да се поврзете и покрај тоа"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Префрлено на <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Уредот користи <xliff:g id="NEW_NETWORK">%1$s</xliff:g> кога <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема пристап до интернет. Може да се наплатат трошоци."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Префрлено од <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"непознат тип мрежа"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System Connectivity Resources"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Најавете се на мрежа на Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Најавете се на мрежа"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема интернет-пристап"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Допрете за опции"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Мобилната мрежа нема интернет-пристап"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Мрежата нема интернет-пристап"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Не може да се пристапи до приватниот DNS-сервер"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничена поврзливост"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Допрете за да се поврзете и покрај тоа"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Префрлено на <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Уредот користи <xliff:g id="NEW_NETWORK">%1$s</xliff:g> кога <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема пристап до интернет. Може да се наплатат трошоци."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Префрлено од <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобилен интернет"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Етернет"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"мобилен интернет"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Етернет"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"непознат тип мрежа"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ml/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ml/strings.xml
index 3e80cf1..8ce7667 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ml/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ml/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"വൈഫൈ നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"മൊബെെൽ നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"സ്വകാര്യ DNS സെർവർ ആക്സസ് ചെയ്യാനാവില്ല"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് പരിമിതമായ കണക്റ്റിവിറ്റി ഉണ്ട്"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ഏതുവിധേനയും കണക്റ്റ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> എന്നതിലേക്ക് മാറി"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-ന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ലാത്തപ്പോൾ ഉപകരണം <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ഉപയോഗിക്കുന്നു. നിരക്കുകൾ ബാധകമായേക്കാം."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> നെറ്റ്വർക്കിൽ നിന്ന് <xliff:g id="NEW_NETWORK">%2$s</xliff:g> നെറ്റ്വർക്കിലേക്ക് മാറി"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"തിരിച്ചറിയാനാകാത്ത ഒരു നെറ്റ്വർക്ക് തരം"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"സിസ്റ്റം കണക്റ്റിവിറ്റി ഉറവിടങ്ങൾ"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"വൈഫൈ നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"മൊബെെൽ നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"നെറ്റ്വർക്കിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"സ്വകാര്യ DNS സെർവർ ആക്സസ് ചെയ്യാനാവില്ല"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് പരിമിതമായ കണക്റ്റിവിറ്റി ഉണ്ട്"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ഏതുവിധേനയും കണക്റ്റ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> എന്നതിലേക്ക് മാറി"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-ന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ലാത്തപ്പോൾ ഉപകരണം <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ഉപയോഗിക്കുന്നു. നിരക്കുകൾ ബാധകമായേക്കാം."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> നെറ്റ്വർക്കിൽ നിന്ന് <xliff:g id="NEW_NETWORK">%2$s</xliff:g> നെറ്റ്വർക്കിലേക്ക് മാറി"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"മൊബൈൽ ഡാറ്റ"</item>
- <item msgid="5520925862115353992">"വൈഫൈ"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"ഇതര്നെറ്റ്"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"മൊബൈൽ ഡാറ്റ"</item>
+ <item msgid="6341719431034774569">"വൈഫൈ"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"ഇതർനെറ്റ്"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"അജ്ഞാതമായ നെറ്റ്വർക്ക് തരം"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mn/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mn/strings.xml
index 214fc0c..be8b592 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mn/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi сүлжээнд нэвтэрнэ үү"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Сүлжээнд нэвтэрнэ үү"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-д интернэтийн хандалт алга"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Сонголт хийхийн тулд товшино уу"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобайл сүлжээнд интернэт хандалт байхгүй байна"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Сүлжээнд интернэт хандалт байхгүй байна"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Хувийн DNS серверт хандах боломжгүй байна"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> зарим үйлчилгээнд хандах боломжгүй байна"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ямар ч тохиолдолд холбогдохын тулд товших"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> руу шилжүүлсэн"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> интернет холболтгүй үед төхөөрөмж <xliff:g id="NEW_NETWORK">%1$s</xliff:g>-г ашигладаг. Төлбөр гарч болзошгүй."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-с <xliff:g id="NEW_NETWORK">%2$s</xliff:g> руу шилжүүлсэн"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"сүлжээний тодорхойгүй төрөл"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Системийн холболтын нөөцүүд"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi сүлжээнд нэвтэрнэ үү"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Сүлжээнд нэвтэрнэ үү"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>-д интернэтийн хандалт алга"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Сонголт хийхийн тулд товшино уу"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Мобайл сүлжээнд интернэт хандалт байхгүй байна"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Сүлжээнд интернэт хандалт байхгүй байна"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Хувийн DNS серверт хандах боломжгүй байна"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> зарим үйлчилгээнд хандах боломжгүй байна"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Ямар ч тохиолдолд холбогдохын тулд товших"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> руу шилжүүлсэн"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> интернет холболтгүй үед төхөөрөмж <xliff:g id="NEW_NETWORK">%1$s</xliff:g>-г ашигладаг. Төлбөр гарч болзошгүй."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-с <xliff:g id="NEW_NETWORK">%2$s</xliff:g> руу шилжүүлсэн"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобайл дата"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Этернэт"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"мобайл дата"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Этернэт"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"үл мэдэгдэх сүлжээний төрөл"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mr/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mr/strings.xml
index c4b1998..fe7df84 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mr/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"वाय-फाय नेटवर्कमध्ये साइन इन करा"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"नेटवर्कवर साइन इन करा"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला इंटरनेट अॅक्सेस नाही"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"पर्यायांसाठी टॅप करा"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"मोबाइल नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"खाजगी DNS सर्व्हर ॲक्सेस करू शकत नाही"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला मर्यादित कनेक्टिव्हिटी आहे"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"तरीही कनेक्ट करण्यासाठी टॅप करा"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> वर स्विच केले"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> कडे इंटरनेटचा अॅक्सेस नसताना डिव्हाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> वापरते. शुल्क लागू शकते."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> वरून <xliff:g id="NEW_NETWORK">%2$s</xliff:g> वर स्विच केले"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"अज्ञात नेटवर्क प्रकार"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"सिस्टम कनेक्टिव्हिटी चे स्रोत"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"वाय-फाय नेटवर्कमध्ये साइन इन करा"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"नेटवर्कवर साइन इन करा"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला इंटरनेट अॅक्सेस नाही"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"पर्यायांसाठी टॅप करा"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"मोबाइल नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"नेटवर्कला इंटरनेट ॲक्सेस नाही"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"खाजगी DNS सर्व्हर ॲक्सेस करू शकत नाही"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ला मर्यादित कनेक्टिव्हिटी आहे"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"तरीही कनेक्ट करण्यासाठी टॅप करा"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> वर स्विच केले"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> कडे इंटरनेटचा अॅक्सेस नसताना डिव्हाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> वापरते. शुल्क लागू शकते."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> वरून <xliff:g id="NEW_NETWORK">%2$s</xliff:g> वर स्विच केले"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"मोबाइल डेटा"</item>
- <item msgid="5520925862115353992">"वाय-फाय"</item>
- <item msgid="1055487873974272842">"ब्लूटूथ"</item>
- <item msgid="1616528372438698248">"इथरनेट"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"मोबाइल डेटा"</item>
+ <item msgid="6341719431034774569">"वाय-फाय"</item>
+ <item msgid="5081440868800877512">"ब्लूटूथ"</item>
+ <item msgid="1160736166977503463">"इथरनेट"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"अज्ञात नेटवर्क प्रकार"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ms/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ms/strings.xml
index 529d0bd..54b49a2 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ms/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ms/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Log masuk ke rangkaian Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Log masuk ke rangkaian"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiada akses Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Ketik untuk mendapatkan pilihan"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Rangkaian mudah alih tiada akses Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Rangkaian tiada akses Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Pelayan DNS peribadi tidak boleh diakses"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> mempunyai kesambungan terhad"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ketik untuk menyambung juga"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Beralih kepada <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Peranti menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> apabila <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tiada akses Internet. Bayaran mungkin dikenakan."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Beralih daripada <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kepada <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"jenis rangkaian tidak diketahui"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Sumber Kesambungan Sistem"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Log masuk ke rangkaian Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Log masuk ke rangkaian"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tiada akses Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Ketik untuk mendapatkan pilihan"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Rangkaian mudah alih tiada akses Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Rangkaian tiada akses Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Pelayan DNS peribadi tidak boleh diakses"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> mempunyai kesambungan terhad"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Ketik untuk menyambung juga"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Beralih kepada <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Peranti menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> apabila <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tiada akses Internet. Bayaran mungkin dikenakan."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Beralih daripada <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kepada <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"data mudah alih"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"data mudah alih"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"jenis rangkaian tidak diketahui"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-my/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-my/strings.xml
index 50590fa..15b75f0 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-my/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-my/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ဝိုင်ဖိုင်ကွန်ရက်သို့ လက်မှတ်ထိုးဝင်ပါ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"အခြားရွေးချယ်စရာများကိုကြည့်ရန် တို့ပါ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"မိုဘိုင်းကွန်ရက်တွင် အင်တာနက်ချိတ်ဆက်မှု မရှိပါ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ကွန်ရက်တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် ချိတ်ဆက်မှုကို ကန့်သတ်ထားသည်"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"မည်သို့ပင်ဖြစ်စေ ချိတ်ဆက်ရန် တို့ပါ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ဖြင့် အင်တာနက် အသုံးမပြုနိုင်သည့်အချိန်တွင် စက်ပစ္စည်းသည် <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ကို သုံးပါသည်။ ဒေတာသုံးစွဲခ ကျသင့်နိုင်ပါသည်။"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> မှ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"အမည်မသိကွန်ရက်အမျိုးအစား"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"စနစ်ချိတ်ဆက်နိုင်မှု ရင်းမြစ်များ"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ဝိုင်ဖိုင်ကွန်ရက်သို့ လက်မှတ်ထိုးဝင်ပါ"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"အခြားရွေးချယ်စရာများကိုကြည့်ရန် တို့ပါ"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"မိုဘိုင်းကွန်ရက်တွင် အင်တာနက်ချိတ်ဆက်မှု မရှိပါ"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"ကွန်ရက်တွင် အင်တာနက်အသုံးပြုခွင့် မရှိပါ"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> တွင် ချိတ်ဆက်မှုကို ကန့်သတ်ထားသည်"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"မည်သို့ပင်ဖြစ်စေ ချိတ်ဆက်ရန် တို့ပါ"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ဖြင့် အင်တာနက် အသုံးမပြုနိုင်သည့်အချိန်တွင် စက်ပစ္စည်းသည် <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ကို သုံးပါသည်။ ဒေတာသုံးစွဲခ ကျသင့်နိုင်ပါသည်။"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> မှ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"မိုဘိုင်းဒေတာ"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"ဘလူးတုသ်"</item>
- <item msgid="1616528372438698248">"အီသာနက်"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"မိုဘိုင်းဒေတာ"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"ဘလူးတုသ်"</item>
+ <item msgid="1160736166977503463">"အီသာနက်"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"အမည်မသိကွန်ရက်အမျိုးအစား"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-nb/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-nb/strings.xml
index b89d198..a561def 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-nb/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-nb/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Logg på Wi-Fi-nettverket"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Logg på nettverk"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internettilkobling"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Trykk for å få alternativer"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilnettverket har ingen internettilgang"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Nettverket har ingen internettilgang"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Den private DNS-tjeneren kan ikke nås"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrenset tilkobling"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Trykk for å koble til likevel"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Byttet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Enheten bruker <xliff:g id="NEW_NETWORK">%1$s</xliff:g> når <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ikke har Internett-tilgang. Avgifter kan påløpe."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Byttet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"en ukjent nettverkstype"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ressurser for systemtilkobling"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Logg på Wi-Fi-nettverket"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Logg på nettverk"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internettilkobling"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Trykk for å få alternativer"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilnettverket har ingen internettilgang"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Nettverket har ingen internettilgang"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Den private DNS-tjeneren kan ikke nås"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begrenset tilkobling"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Trykk for å koble til likevel"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Byttet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Enheten bruker <xliff:g id="NEW_NETWORK">%1$s</xliff:g> når <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ikke har Internett-tilgang. Avgifter kan påløpe."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Byttet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobildata"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobildata"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"en ukjent nettverkstype"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ne/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ne/strings.xml
index bdcfa3b..f74542d 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ne/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ne/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi नेटवर्कमा साइन इन गर्नुहोस्"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"सञ्जालमा साइन इन गर्नुहोस्"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को इन्टरनेटमाथि पहुँच छैन"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"विकल्पहरूका लागि ट्याप गर्नुहोस्"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"मोबाइल नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को जडान सीमित छ"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"जसरी भए पनि जडान गर्न ट्याप गर्नुहोस्"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> मा बदल्नुहोस्"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> मार्फत इन्टरनेटमाथि पहुँच राख्न नसकेको अवस्थामा यन्त्रले <xliff:g id="NEW_NETWORK">%1$s</xliff:g> प्रयोग गर्दछ। शुल्क लाग्न सक्छ।"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> बाट <xliff:g id="NEW_NETWORK">%2$s</xliff:g> मा परिवर्तन गरियो"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"नेटवर्कको कुनै अज्ञात प्रकार"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"सिस्टम कनेक्टिभिटीका स्रोतहरू"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi नेटवर्कमा साइन इन गर्नुहोस्"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"सञ्जालमा साइन इन गर्नुहोस्"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को इन्टरनेटमाथि पहुँच छैन"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"विकल्पहरूका लागि ट्याप गर्नुहोस्"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"मोबाइल नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"नेटवर्कको इन्टरनेटमाथि पहुँच छैन"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> को जडान सीमित छ"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"जसरी भए पनि जडान गर्न ट्याप गर्नुहोस्"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> मा बदल्नुहोस्"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> मार्फत इन्टरनेटमाथि पहुँच राख्न नसकेको अवस्थामा यन्त्रले <xliff:g id="NEW_NETWORK">%1$s</xliff:g> प्रयोग गर्दछ। शुल्क लाग्न सक्छ।"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> बाट <xliff:g id="NEW_NETWORK">%2$s</xliff:g> मा परिवर्तन गरियो"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"मोबाइल डेटा"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"ब्लुटुथ"</item>
- <item msgid="1616528372438698248">"इथरनेट"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"मोबाइल डेटा"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"ब्लुटुथ"</item>
+ <item msgid="1160736166977503463">"इथरनेट"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"नेटवर्कको कुनै अज्ञात प्रकार"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-nl/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-nl/strings.xml
index 8ecff6e..0f3203b 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-nl/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-nl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Inloggen bij wifi-netwerk"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Inloggen bij netwerk"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft geen internettoegang"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tik voor opties"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobiel netwerk heeft geen internettoegang"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Netwerk heeft geen internettoegang"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Geen toegang tot privé-DNS-server"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft beperkte connectiviteit"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tik om toch verbinding te maken"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Overgeschakeld naar <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Apparaat gebruikt <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internetverbinding heeft. Er kunnen kosten in rekening worden gebracht."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Overgeschakeld van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> naar <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"een onbekend netwerktype"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resources voor systeemconnectiviteit"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Inloggen bij wifi-netwerk"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Inloggen bij netwerk"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft geen internettoegang"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tik voor opties"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobiel netwerk heeft geen internettoegang"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Netwerk heeft geen internettoegang"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Geen toegang tot privé-DNS-server"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> heeft beperkte connectiviteit"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tik om toch verbinding te maken"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Overgeschakeld naar <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Apparaat gebruikt <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internetverbinding heeft. Er kunnen kosten in rekening worden gebracht."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Overgeschakeld van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> naar <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobiele data"</item>
- <item msgid="5520925862115353992">"Wifi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobiele data"</item>
+ <item msgid="6341719431034774569">"Wifi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"een onbekend netwerktype"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-or/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-or/strings.xml
index 6ec1f9d..ecf4d69 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-or/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-or/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ୱାଇ-ଫାଇ ନେଟୱର୍କରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ନେଟ୍ୱର୍କରେ ସାଇନ୍ ଇନ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ବିକଳ୍ପ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ମୋବାଇଲ୍ ନେଟ୍ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ନେଟ୍ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ବ୍ୟକ୍ତିଗତ DNS ସର୍ଭର୍ ଆକ୍ସେସ୍ କରିହେବ ନାହିଁ"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ସୀମିତ ସଂଯୋଗ ଅଛି"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ତଥାପି ଯୋଗାଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ର ଇଣ୍ଟରନେଟ୍ ଆକ୍ସେସ୍ ନଥିବାବେଳେ ଡିଭାଇସ୍ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ବ୍ୟବହାର କରିଥାଏ। ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ।"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ରୁ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ଏକ ଅଜଣା ନେଟ୍ୱର୍କ ପ୍ରକାର"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ସିଷ୍ଟମର ସଂଯୋଗ ସମ୍ବନ୍ଧିତ ରିସୋର୍ସଗୁଡ଼ିକ"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ୱାଇ-ଫାଇ ନେଟୱର୍କରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ନେଟ୍ୱର୍କରେ ସାଇନ୍ ଇନ୍ କରନ୍ତୁ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ବିକଳ୍ପ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"ମୋବାଇଲ୍ ନେଟ୍ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"ନେଟ୍ୱାର୍କରେ ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ବ୍ୟକ୍ତିଗତ DNS ସର୍ଭର୍ ଆକ୍ସେସ୍ କରିହେବ ନାହିଁ"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>ର ସୀମିତ ସଂଯୋଗ ଅଛି"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ତଥାପି ଯୋଗାଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ର ଇଣ୍ଟରନେଟ୍ ଆକ୍ସେସ୍ ନଥିବାବେଳେ ଡିଭାଇସ୍ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ବ୍ୟବହାର କରିଥାଏ। ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ।"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ରୁ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ମୋବାଇଲ୍ ଡାଟା"</item>
- <item msgid="5520925862115353992">"ୱାଇ-ଫାଇ"</item>
- <item msgid="1055487873974272842">"ବ୍ଲୁଟୁଥ"</item>
- <item msgid="1616528372438698248">"ଇଥରନେଟ୍"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"ମୋବାଇଲ ଡାଟା"</item>
+ <item msgid="6341719431034774569">"ୱାଇ-ଫାଇ"</item>
+ <item msgid="5081440868800877512">"ବ୍ଲୁଟୁଥ୍"</item>
+ <item msgid="1160736166977503463">"ଇଥରନେଟ୍"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"ଏକ ଅଜଣା ନେଟୱାର୍କ ପ୍ରକାର"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pa/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pa/strings.xml
index e948193..4328054 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pa/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਸੀਮਤ ਕਨੈਕਟੀਵਿਟੀ ਹੈ"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ਫਿਰ ਵੀ ਕਨੈਕਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"ਬਦਲਕੇ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ਲਿਆਂਦਾ ਗਿਆ"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ਦੀ ਇੰਟਰਨੈੱਟ \'ਤੇ ਪਹੁੰਚ ਨਾ ਹੋਣ \'ਤੇ ਡੀਵਾਈਸ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ। ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ।"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ਤੋਂ ਬਦਲਕੇ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> \'ਤੇ ਕੀਤਾ ਗਿਆ"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ਇੱਕ ਅਗਿਆਤ ਨੈੱਟਵਰਕ ਕਿਸਮ"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ਸਿਸਟਮ ਕਨੈਕਟੀਵਿਟੀ ਸਰੋਤ"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"ਨੈੱਟਵਰਕ ਕੋਲ ਇੰਟਰਨੈੱਟ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ਕੋਲ ਸੀਮਤ ਕਨੈਕਟੀਵਿਟੀ ਹੈ"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ਫਿਰ ਵੀ ਕਨੈਕਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"ਬਦਲਕੇ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ਲਿਆਂਦਾ ਗਿਆ"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ਦੀ ਇੰਟਰਨੈੱਟ \'ਤੇ ਪਹੁੰਚ ਨਾ ਹੋਣ \'ਤੇ ਡੀਵਾਈਸ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ। ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ।"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ਤੋਂ ਬਦਲਕੇ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> \'ਤੇ ਕੀਤਾ ਗਿਆ"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ਮੋਬਾਈਲ ਡਾਟਾ"</item>
- <item msgid="5520925862115353992">"ਵਾਈ-ਫਾਈ"</item>
- <item msgid="1055487873974272842">"ਬਲੂਟੁੱਥ"</item>
- <item msgid="1616528372438698248">"ਈਥਰਨੈੱਟ"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"ਮੋਬਾਈਲ ਡਾਟਾ"</item>
+ <item msgid="6341719431034774569">"ਵਾਈ-ਫਾਈ"</item>
+ <item msgid="5081440868800877512">"ਬਲੂਟੁੱਥ"</item>
+ <item msgid="1160736166977503463">"ਈਥਰਨੈੱਟ"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"ਕੋਈ ਅਗਿਆਤ ਨੈੱਟਵਰਕ ਦੀ ਕਿਸਮ"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pl/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pl/strings.xml
index 038328f..e6b3a0c 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pl/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Zaloguj się w sieci Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Zaloguj się do sieci"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nie ma dostępu do internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Kliknij, by wyświetlić opcje"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Sieć komórkowa nie ma dostępu do internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Sieć nie ma dostępu do internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Brak dostępu do prywatnego serwera DNS"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ma ograniczoną łączność"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Kliknij, by mimo to nawiązać połączenie"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Zmieniono na połączenie typu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Urządzenie korzysta z połączenia typu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, gdy <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nie dostępu do internetu. Mogą zostać naliczone opłaty."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Przełączono z połączenia typu <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>."</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nieznany typ sieci"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Zasoby systemowe dotyczące łączności"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Zaloguj się w sieci Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Zaloguj się do sieci"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nie ma dostępu do internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Kliknij, by wyświetlić opcje"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Sieć komórkowa nie ma dostępu do internetu"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Sieć nie ma dostępu do internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Brak dostępu do prywatnego serwera DNS"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ma ograniczoną łączność"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Kliknij, by mimo to nawiązać połączenie"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Zmieniono na połączenie typu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Urządzenie korzysta z połączenia typu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, gdy <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nie dostępu do internetu. Mogą zostać naliczone opłaty."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Przełączono z połączenia typu <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>."</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilna transmisja danych"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobilna transmisja danych"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"nieznany typ sieci"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml
index fe37405..f1d0bc0 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rBR/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Fazer login na rede Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Fazer login na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toque para ver opções"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"A rede móvel não tem acesso à Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede não tem acesso à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Não é possível acessar o servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toque para conectar mesmo assim"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"um tipo de rede desconhecido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conectividade do sistema"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Fazer login na rede Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Fazer login na rede"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toque para ver opções"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"A rede móvel não tem acesso à Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"A rede não tem acesso à Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Não é possível acessar o servidor DNS privado"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toque para conectar mesmo assim"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dados móveis"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"dados móveis"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"um tipo de rede desconhecido"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml
index 69060f7..163d70b 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt-rPT/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Iniciar sessão na rede Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Início de sessão na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toque para obter mais opções"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"A rede móvel não tem acesso à Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede não tem acesso à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Não é possível aceder ao servidor DNS."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conetividade limitada."</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toque para ligar mesmo assim."</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Mudou para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Podem aplicar-se custos."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Mudou de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"um tipo de rede desconhecido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conetividade do sistema"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Iniciar sessão na rede Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Início de sessão na rede"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toque para obter mais opções"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"A rede móvel não tem acesso à Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"A rede não tem acesso à Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Não é possível aceder ao servidor DNS."</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conetividade limitada."</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toque para ligar mesmo assim."</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Mudou para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Podem aplicar-se custos."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Mudou de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dados móveis"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"dados móveis"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"um tipo de rede desconhecido"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt/strings.xml
index fe37405..f1d0bc0 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-pt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Fazer login na rede Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Fazer login na rede"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Toque para ver opções"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"A rede móvel não tem acesso à Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"A rede não tem acesso à Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Não é possível acessar o servidor DNS privado"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Toque para conectar mesmo assim"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"um tipo de rede desconhecido"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Recursos de conectividade do sistema"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Fazer login na rede Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Fazer login na rede"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> não tem acesso à Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Toque para ver opções"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"A rede móvel não tem acesso à Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"A rede não tem acesso à Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Não é possível acessar o servidor DNS privado"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> tem conectividade limitada"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Toque para conectar mesmo assim"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dados móveis"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"dados móveis"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"um tipo de rede desconhecido"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ro/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ro/strings.xml
index 227b7be..221261c 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ro/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ro/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Conectați-vă la rețeaua Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Conectați-vă la rețea"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nu are acces la internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Atingeți pentru opțiuni"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Rețeaua mobilă nu are acces la internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Rețeaua nu are acces la internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Serverul DNS privat nu poate fi accesat"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> are conectivitate limitată"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Atingeți pentru a vă conecta oricum"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"S-a comutat la <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Dispozitivul folosește <xliff:g id="NEW_NETWORK">%1$s</xliff:g> când <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nu are acces la internet. Se pot aplica taxe."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"S-a comutat de la <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> la <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tip de rețea necunoscut"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resurse pentru conectivitatea sistemului"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Conectați-vă la rețeaua Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Conectați-vă la rețea"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nu are acces la internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Atingeți pentru opțiuni"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Rețeaua mobilă nu are acces la internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Rețeaua nu are acces la internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Serverul DNS privat nu poate fi accesat"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> are conectivitate limitată"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Atingeți pentru a vă conecta oricum"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"S-a comutat la <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Dispozitivul folosește <xliff:g id="NEW_NETWORK">%1$s</xliff:g> când <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nu are acces la internet. Se pot aplica taxe."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"S-a comutat de la <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> la <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"date mobile"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"date mobile"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"un tip de rețea necunoscut"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ru/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ru/strings.xml
index 18acf81..ba179b7 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ru/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ru/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Подключение к Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Регистрация в сети"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Сеть \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" не подключена к Интернету"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Нажмите, чтобы показать варианты."</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобильная сеть не подключена к Интернету"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Сеть не подключена к Интернету"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Доступа к частному DNS-серверу нет."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Подключение к сети \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" ограничено"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Нажмите, чтобы подключиться"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Новое подключение: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Устройство использует <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, если подключение к сети <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> недоступно. Может взиматься плата за передачу данных."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Устройство отключено от сети <xliff:g id="NEW_NETWORK">%2$s</xliff:g> и теперь использует <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"неизвестный тип сети"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"System Connectivity Resources"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Подключение к Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Регистрация в сети"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Сеть \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" не подключена к Интернету"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Нажмите, чтобы показать варианты."</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Мобильная сеть не подключена к Интернету"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Сеть не подключена к Интернету"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Доступа к частному DNS-серверу нет."</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Подключение к сети \"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>\" ограничено"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Нажмите, чтобы подключиться"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Новое подключение: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Устройство использует <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, если подключение к сети <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> недоступно. Может взиматься плата за передачу данных."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Устройство отключено от сети <xliff:g id="NEW_NETWORK">%2$s</xliff:g> и теперь использует <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобильный Интернет"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"мобильный интернет"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"неизвестный тип сети"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-si/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-si/strings.xml
index 6307c2b..1c493a7 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-si/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-si/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi ජාලයට පුරනය වන්න"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ජාලයට පුරනය වන්න"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට අන්තර්ජාල ප්රවේශය නැත"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"විකල්ප සඳහා තට්ටු කරන්න"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"ජංගම ජාලවලට අන්තර්ජාල ප්රවේශය නැත"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"ජාලයට අන්තර්ජාල ප්රවේශය නැත"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"පුද්ගලික DNS සේවාදායකයට ප්රවේශ වීමට නොහැකිය"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට සීමිත සබැඳුම් හැකියාවක් ඇත"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"කෙසේ වෙතත් ඉදිරියට යාමට තට්ටු කරන්න"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> වෙත මාරු විය"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"උපාංගය <xliff:g id="NEW_NETWORK">%1$s</xliff:g> <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> සඳහා අන්තර්ජාල ප්රවේශය නැති විට භාවිත කරයි. ගාස්තු අදාළ විය හැකිය."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> සිට <xliff:g id="NEW_NETWORK">%2$s</xliff:g> වෙත මාරු විය"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"නොදන්නා ජාල වර්ගයකි"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"පද්ධති සබැඳුම් හැකියා සම්පත්"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi ජාලයට පුරනය වන්න"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ජාලයට පුරනය වන්න"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට අන්තර්ජාල ප්රවේශය නැත"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"විකල්ප සඳහා තට්ටු කරන්න"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"ජංගම ජාලවලට අන්තර්ජාල ප්රවේශය නැත"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"ජාලයට අන්තර්ජාල ප්රවේශය නැත"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"පුද්ගලික DNS සේවාදායකයට ප්රවේශ වීමට නොහැකිය"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> හට සීමිත සබැඳුම් හැකියාවක් ඇත"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"කෙසේ වෙතත් ඉදිරියට යාමට තට්ටු කරන්න"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> වෙත මාරු විය"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"උපාංගය <xliff:g id="NEW_NETWORK">%1$s</xliff:g> <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> සඳහා අන්තර්ජාල ප්රවේශය නැති විට භාවිත කරයි. ගාස්තු අදාළ විය හැකිය."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> සිට <xliff:g id="NEW_NETWORK">%2$s</xliff:g> වෙත මාරු විය"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"ජංගම දත්ත"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"බ්ලූටූත්"</item>
- <item msgid="1616528372438698248">"ඊතර්නෙට්"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"ජංගම දත්ත"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"බ්ලූටූත්"</item>
+ <item msgid="1160736166977503463">"ඊතර්නෙට්"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"නොදන්නා ජාල වර්ගයකි"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sk/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sk/strings.xml
index e894fef..1b9313a 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sk/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prihlásiť sa do siete Wi‑Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prihlásenie do siete"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá prístup k internetu"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Klepnutím získate možnosti"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilná sieť nemá prístup k internetu"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Sieť nemá prístup k internetu"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> má obmedzené pripojenie"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Ak sa chcete aj napriek tomu pripojiť, klepnite"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Prepnuté na sieť: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Keď <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nemá prístup k internetu, zariadenie používa <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Môžu sa účtovať poplatky."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Prepnuté zo siete <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sieť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"neznámy typ siete"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Zdroje možností pripojenia systému"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prihlásiť sa do siete Wi‑Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Prihlásenie do siete"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nemá prístup k internetu"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Klepnutím získate možnosti"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilná sieť nemá prístup k internetu"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Sieť nemá prístup k internetu"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> má obmedzené pripojenie"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Ak sa chcete aj napriek tomu pripojiť, klepnite"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Prepnuté na sieť: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Keď <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nemá prístup k internetu, zariadenie používa <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Môžu sa účtovať poplatky."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Prepnuté zo siete <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sieť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobilné dáta"</item>
- <item msgid="5520925862115353992">"Wi‑Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobilné dáta"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"neznámy typ siete"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sl/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sl/strings.xml
index 954b324..739fb8e 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sl/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavite se v omrežje Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Prijava v omrežje"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Omrežje <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nima dostopa do interneta"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Dotaknite se za možnosti"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilno omrežje nima dostopa do interneta"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Omrežje nima dostopa do interneta"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Povezljivost omrežja <xliff:g id="NETWORK_SSID">%1$s</xliff:g> je omejena"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Dotaknite se, da kljub temu vzpostavite povezavo"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Preklopljeno na omrežje vrste <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Naprava uporabi omrežje vrste <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, ko omrežje vrste <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nima dostopa do interneta. Prenos podatkov se lahko zaračuna."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Preklopljeno z omrežja vrste <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na omrežje vrste <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"neznana vrsta omrežja"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Viri povezljivosti sistema"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Prijavite se v omrežje Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Prijava v omrežje"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Omrežje <xliff:g id="NETWORK_SSID">%1$s</xliff:g> nima dostopa do interneta"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Dotaknite se za možnosti"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilno omrežje nima dostopa do interneta"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Omrežje nima dostopa do interneta"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Povezljivost omrežja <xliff:g id="NETWORK_SSID">%1$s</xliff:g> je omejena"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Dotaknite se, da kljub temu vzpostavite povezavo"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Preklopljeno na omrežje vrste <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Naprava uporabi omrežje vrste <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, ko omrežje vrste <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nima dostopa do interneta. Prenos podatkov se lahko zaračuna."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Preklopljeno z omrežja vrste <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na omrežje vrste <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"prenos podatkov v mobilnem omrežju"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"prenos podatkov v mobilnem omrežju"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"neznana vrsta omrežja"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sq/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sq/strings.xml
index bd5d052..cf8cf3b 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sq/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sq/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Identifikohu në rrjetin Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Identifikohu në rrjet"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nuk ka qasje në internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Trokit për opsionet"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Rrjeti celular nuk ka qasje në internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Rrjeti nuk ka qasje në internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Serveri privat DNS nuk mund të qaset"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ka lidhshmëri të kufizuar"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Trokit për t\'u lidhur gjithsesi"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Kaloi te <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Pajisja përdor <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kur <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nuk ka qasje në internet. Mund të zbatohen tarifa."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Kaloi nga <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> te <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"një lloj rrjeti i panjohur"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Burimet e lidhshmërisë së sistemit"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Identifikohu në rrjetin Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Identifikohu në rrjet"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nuk ka qasje në internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Trokit për opsionet"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Rrjeti celular nuk ka qasje në internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Rrjeti nuk ka qasje në internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Serveri privat DNS nuk mund të qaset"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ka lidhshmëri të kufizuar"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Trokit për t\'u lidhur gjithsesi"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Kaloi te <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Pajisja përdor <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kur <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nuk ka qasje në internet. Mund të zbatohen tarifa."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Kaloi nga <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> te <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"të dhënat celulare"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Eternet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"të dhënat celulare"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Eternet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"një lloj rrjeti i panjohur"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sr/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sr/strings.xml
index 8bedbff..1f7c95c 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sr/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Пријављивање на WiFi мрежу"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Пријавите се на мрежу"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема приступ интернету"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Додирните за опције"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобилна мрежа нема приступ интернету"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Мрежа нема приступ интернету"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Приступ приватном DNS серверу није успео"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничену везу"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Додирните да бисте се ипак повезали"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Прешли сте на тип мреже <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Уређај користи тип мреже <xliff:g id="NEW_NETWORK">%1$s</xliff:g> када тип мреже <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема приступ интернету. Можда ће се наплаћивати трошкови."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Прешли сте са типа мреже <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на тип мреже <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"непознат тип мреже"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ресурси за повезивање са системом"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Пријављивање на WiFi мрежу"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Пријавите се на мрежу"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> нема приступ интернету"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Додирните за опције"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Мобилна мрежа нема приступ интернету"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Мрежа нема приступ интернету"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Приступ приватном DNS серверу није успео"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> има ограничену везу"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Додирните да бисте се ипак повезали"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Прешли сте на тип мреже <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Уређај користи тип мреже <xliff:g id="NEW_NETWORK">%1$s</xliff:g> када тип мреже <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема приступ интернету. Можда ће се наплаћивати трошкови."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Прешли сте са типа мреже <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на тип мреже <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобилни подаци"</item>
- <item msgid="5520925862115353992">"WiFi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Етернет"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"мобилни подаци"</item>
+ <item msgid="6341719431034774569">"WiFi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Етернет"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"непознат тип мреже"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sv/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sv/strings.xml
index b3f1763..7314005 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sv/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Logga in på ett Wi-Fi-nätverk"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Logga in på nätverket"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetanslutning"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Tryck för alternativ"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobilnätverket har ingen internetanslutning"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Nätverket har ingen internetanslutning"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Det går inte att komma åt den privata DNS-servern."</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begränsad anslutning"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Tryck för att ansluta ändå"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Byte av nätverk till <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> används på enheten när det inte finns internetåtkomst via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Avgifter kan tillkomma."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Byte av nätverk från <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> till <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"en okänd nätverkstyp"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Resurser för systemanslutning"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Logga in på ett Wi-Fi-nätverk"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Logga in på nätverket"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har ingen internetanslutning"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Tryck för alternativ"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobilnätverket har ingen internetanslutning"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Nätverket har ingen internetanslutning"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Det går inte att komma åt den privata DNS-servern."</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> har begränsad anslutning"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Tryck för att ansluta ändå"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Byte av nätverk till <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> används på enheten när det inte finns internetåtkomst via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Avgifter kan tillkomma."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Byte av nätverk från <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> till <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobildata"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobildata"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"en okänd nätverkstyp"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sw/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sw/strings.xml
index 9674654..5c4d594 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-sw/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-sw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Ingia kwa mtandao wa Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Ingia katika mtandao"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> haina uwezo wa kufikia intaneti"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Gusa ili upate chaguo"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mtandao wa simu hauna uwezo wa kufikia intaneti"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mtandao hauna uwezo wa kufikia intaneti"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ina muunganisho unaofikia huduma chache."</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Gusa ili uunganishe tu"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Kifaa hutumia <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wakati <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> haina intaneti. Huenda ukalipishwa."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Imebadilisha mtandao kutoka <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sasa inatumia <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"aina ya mtandao isiyojulikana"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Nyenzo za Muunganisho wa Mfumo"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Ingia kwa mtandao wa Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Ingia katika mtandao"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> haina uwezo wa kufikia intaneti"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Gusa ili upate chaguo"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mtandao wa simu hauna uwezo wa kufikia intaneti"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Mtandao hauna uwezo wa kufikia intaneti"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ina muunganisho unaofikia huduma chache."</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Gusa ili uunganishe tu"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Kifaa hutumia <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wakati <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> haina intaneti. Huenda ukalipishwa."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Imebadilisha mtandao kutoka <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sasa inatumia <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"data ya simu"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethaneti"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"data ya mtandao wa simu"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethaneti"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"aina ya mtandao isiyojulikana"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ta/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ta/strings.xml
index 12604cb..90f89c9 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ta/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ta/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"வைஃபை நெட்வொர்க்கில் உள்நுழையவும்"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"நெட்வொர்க்கில் உள்நுழையவும்"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"விருப்பங்களுக்கு, தட்டவும்"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"மொபைல் நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> வரம்பிற்கு உட்பட்ட இணைப்புநிலையைக் கொண்டுள்ளது"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"எப்படியேனும் இணைப்பதற்குத் தட்டவும்"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> நெட்வொர்க்கில் இண்டர்நெட் அணுகல் இல்லாததால், சாதனமானது <xliff:g id="NEW_NETWORK">%1$s</xliff:g> நெட்வொர்க்கைப் பயன்படுத்துகிறது. கட்டணங்கள் விதிக்கப்படலாம்."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> இலிருந்து <xliff:g id="NEW_NETWORK">%2$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"தெரியாத நெட்வொர்க் வகை"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"சிஸ்டம் இணைப்பு மூலங்கள்"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"வைஃபை நெட்வொர்க்கில் உள்நுழையவும்"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"நெட்வொர்க்கில் உள்நுழையவும்"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"விருப்பங்களுக்கு, தட்டவும்"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"மொபைல் நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"நெட்வொர்க்கிற்கு இணைய அணுகல் இல்லை"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> வரம்பிற்கு உட்பட்ட இணைப்புநிலையைக் கொண்டுள்ளது"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"எப்படியேனும் இணைப்பதற்குத் தட்டவும்"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> நெட்வொர்க்கில் இண்டர்நெட் அணுகல் இல்லாததால், சாதனமானது <xliff:g id="NEW_NETWORK">%1$s</xliff:g> நெட்வொர்க்கைப் பயன்படுத்துகிறது. கட்டணங்கள் விதிக்கப்படலாம்."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> இலிருந்து <xliff:g id="NEW_NETWORK">%2$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"மொபைல் டேட்டா"</item>
- <item msgid="5520925862115353992">"வைஃபை"</item>
- <item msgid="1055487873974272842">"புளூடூத்"</item>
- <item msgid="1616528372438698248">"ஈத்தர்நெட்"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"மொபைல் டேட்டா"</item>
+ <item msgid="6341719431034774569">"வைஃபை"</item>
+ <item msgid="5081440868800877512">"புளூடூத்"</item>
+ <item msgid="1160736166977503463">"ஈதர்நெட்"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"தெரியாத நெட்வொர்க் வகை"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-te/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-te/strings.xml
index 84a8640..c69b599 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-te/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-te/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"ఎంపికల కోసం నొక్కండి"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"మొబైల్ నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"ప్రైవేట్ DNS సర్వర్ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> పరిమిత కనెక్టివిటీని కలిగి ఉంది"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"ఏదేమైనా కనెక్ట్ చేయడానికి నొక్కండి"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>కి మార్చబడింది"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"పరికరం <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేనప్పుడు <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ని ఉపయోగిస్తుంది. ఛార్జీలు వర్తించవచ్చు."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> నుండి <xliff:g id="NEW_NETWORK">%2$s</xliff:g>కి మార్చబడింది"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"తెలియని నెట్వర్క్ రకం"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"సిస్టమ్ కనెక్టివిటీ రిసోర్స్లు"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"ఎంపికల కోసం నొక్కండి"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"మొబైల్ నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"నెట్వర్క్కు ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"ప్రైవేట్ DNS సర్వర్ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> పరిమిత కనెక్టివిటీని కలిగి ఉంది"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"ఏదేమైనా కనెక్ట్ చేయడానికి నొక్కండి"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>కి మార్చబడింది"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"పరికరం <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేనప్పుడు <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ని ఉపయోగిస్తుంది. ఛార్జీలు వర్తించవచ్చు."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> నుండి <xliff:g id="NEW_NETWORK">%2$s</xliff:g>కి మార్చబడింది"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"మొబైల్ డేటా"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"బ్లూటూత్"</item>
- <item msgid="1616528372438698248">"ఈథర్నెట్"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"మొబైల్ డేటా"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"బ్లూటూత్"</item>
+ <item msgid="1160736166977503463">"ఈథర్నెట్"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"తెలియని నెట్వర్క్ రకం"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-th/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-th/strings.xml
index 1616e51..eee5a35 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-th/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-th/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"ลงชื่อเข้าใช้เครือข่าย WiFi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"ลงชื่อเข้าใช้เครือข่าย"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"แตะเพื่อดูตัวเลือก"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"เครือข่ายมือถือไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"เครือข่ายไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> มีการเชื่อมต่อจำกัด"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"แตะเพื่อเชื่อมต่อ"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"เปลี่ยนเป็น <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"อุปกรณ์จะใช้ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> เมื่อ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้ โดยอาจมีค่าบริการ"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"เปลี่ยนจาก <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> เป็น <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"ประเภทเครือข่ายที่ไม่รู้จัก"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"ทรัพยากรการเชื่อมต่อของระบบ"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"ลงชื่อเข้าใช้เครือข่าย WiFi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"ลงชื่อเข้าใช้เครือข่าย"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"แตะเพื่อดูตัวเลือก"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"เครือข่ายมือถือไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"เครือข่ายไม่มีการเข้าถึงอินเทอร์เน็ต"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> มีการเชื่อมต่อจำกัด"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"แตะเพื่อเชื่อมต่อ"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"เปลี่ยนเป็น <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"อุปกรณ์จะใช้ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> เมื่อ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้ โดยอาจมีค่าบริการ"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"เปลี่ยนจาก <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> เป็น <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"เน็ตมือถือ"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"บลูทูธ"</item>
- <item msgid="1616528372438698248">"อีเทอร์เน็ต"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"อินเทอร์เน็ตมือถือ"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"บลูทูธ"</item>
+ <item msgid="1160736166977503463">"อีเทอร์เน็ต"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"ประเภทเครือข่ายที่ไม่รู้จัก"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-tl/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-tl/strings.xml
index 3bf1ce4..8d665fe 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-tl/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-tl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Mag-sign in sa Wi-Fi network"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Mag-sign in sa network"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Walang access sa internet ang <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"I-tap para sa mga opsyon"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Walang access sa internet ang mobile network"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Walang access sa internet ang network"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Hindi ma-access ang pribadong DNS server"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Limitado ang koneksyon ng <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"I-tap para kumonekta pa rin"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Lumipat sa <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Ginagamit ng device ang <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kapag walang access sa internet ang <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Maaaring may mga malapat na singilin."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Lumipat sa <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mula sa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"isang hindi kilalang uri ng network"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Mga Resource ng Pagkakonekta ng System"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Mag-sign in sa Wi-Fi network"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Mag-sign in sa network"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Walang access sa internet ang <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"I-tap para sa mga opsyon"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Walang access sa internet ang mobile network"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Walang access sa internet ang network"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Hindi ma-access ang pribadong DNS server"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Limitado ang koneksyon ng <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"I-tap para kumonekta pa rin"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Lumipat sa <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Ginagamit ng device ang <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kapag walang access sa internet ang <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Maaaring may mga malapat na singilin."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Lumipat sa <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mula sa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobile data"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobile data"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"isang hindi kilalang uri ng network"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-tr/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-tr/strings.xml
index 5c326e5..cfb7632 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-tr/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-tr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Kablosuz ağda oturum açın"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Ağda oturum açın"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ağının internet bağlantısı yok"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Seçenekler için dokunun"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobil ağın internet bağlantısı yok"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Ağın internet bağlantısı yok"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Gizli DNS sunucusuna erişilemiyor"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sınırlı bağlantıya sahip"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Yine de bağlanmak için dokunun"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ağına geçildi"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ağının internet erişimi olmadığında cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ağını kullanır. Bunun için ödeme alınabilir."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ağından <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ağına geçildi"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"bilinmeyen ağ türü"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Sistem Bağlantı Kaynakları"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Kablosuz ağda oturum açın"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Ağda oturum açın"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ağının internet bağlantısı yok"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Seçenekler için dokunun"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobil ağın internet bağlantısı yok"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Ağın internet bağlantısı yok"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Gizli DNS sunucusuna erişilemiyor"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sınırlı bağlantıya sahip"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Yine de bağlanmak için dokunun"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ağına geçildi"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ağının internet erişimi olmadığında cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ağını kullanır. Bunun için ödeme alınabilir."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ağından <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ağına geçildi"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobil veri"</item>
- <item msgid="5520925862115353992">"Kablosuz"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobil veri"</item>
+ <item msgid="6341719431034774569">"Kablosuz"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"bilinmeyen ağ türü"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-uk/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-uk/strings.xml
index d1382da..c5da746 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-uk/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-uk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Вхід у мережу Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Вхід у мережу"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"Мережа <xliff:g id="NETWORK_SSID">%1$s</xliff:g> не має доступу до Інтернету"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Торкніться, щоб відкрити опції"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Мобільна мережа не має доступу до Інтернету"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Мережа не має доступу до Інтернету"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Немає доступу до приватного DNS-сервера"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"Підключення до мережі <xliff:g id="NETWORK_SSID">%1$s</xliff:g> обмежено"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Натисніть, щоб усе одно підключитися"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Пристрій перейшов на мережу <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Коли мережа <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> не має доступу до Інтернету, використовується <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Може стягуватися плата."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Пристрій перейшов з мережі <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на мережу <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"невідомий тип мережі"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Ресурси для підключення системи"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Вхід у мережу Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Вхід у мережу"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"Мережа <xliff:g id="NETWORK_SSID">%1$s</xliff:g> не має доступу до Інтернету"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Торкніться, щоб відкрити опції"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Мобільна мережа не має доступу до Інтернету"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Мережа не має доступу до Інтернету"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Немає доступу до приватного DNS-сервера"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"Підключення до мережі <xliff:g id="NETWORK_SSID">%1$s</xliff:g> обмежено"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Натисніть, щоб усе одно підключитися"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Пристрій перейшов на мережу <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Коли мережа <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> не має доступу до Інтернету, використовується <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Може стягуватися плата."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Пристрій перейшов з мережі <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на мережу <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобільне передавання даних"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"Мережа VPN"</item>
+ <item msgid="5454013645032700715">"мобільний Інтернет"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"невідомий тип мережі"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ur/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ur/strings.xml
index 3c031ad..bd2a228 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-ur/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-ur/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi نیٹ ورک میں سائن ان کریں"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"نیٹ ورک میں سائن ان کریں"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"اختیارات کیلئے تھپتھپائیں"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"موبائل نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"نجی DNS سرور تک رسائی حاصل نہیں کی جا سکی"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کی کنیکٹوٹی محدود ہے"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"بہر حال منسلک کرنے کے لیے تھپتھپائیں"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> پر سوئچ ہو گیا"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"جب <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> کو انٹرنیٹ تک رسائی نہیں ہوتی ہے تو آلہ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> کا استعمال کرتا ہے۔ چارجز لاگو ہو سکتے ہیں۔"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> سے <xliff:g id="NEW_NETWORK">%2$s</xliff:g> پر سوئچ ہو گیا"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"نیٹ ورک کی نامعلوم قسم"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"سسٹم کنیکٹوٹی کے وسائل"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi نیٹ ورک میں سائن ان کریں"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"نیٹ ورک میں سائن ان کریں"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"اختیارات کیلئے تھپتھپائیں"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"موبائل نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"نیٹ ورک کو انٹرنیٹ تک رسائی حاصل نہیں ہے"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"نجی DNS سرور تک رسائی حاصل نہیں کی جا سکی"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> کی کنیکٹوٹی محدود ہے"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"بہر حال منسلک کرنے کے لیے تھپتھپائیں"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> پر سوئچ ہو گیا"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"جب <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> کو انٹرنیٹ تک رسائی نہیں ہوتی ہے تو آلہ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> کا استعمال کرتا ہے۔ چارجز لاگو ہو سکتے ہیں۔"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> سے <xliff:g id="NEW_NETWORK">%2$s</xliff:g> پر سوئچ ہو گیا"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"موبائل ڈیٹا"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"بلوٹوتھ"</item>
- <item msgid="1616528372438698248">"ایتھرنیٹ"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"موبائل ڈیٹا"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"بلوٹوتھ"</item>
+ <item msgid="1160736166977503463">"ایتھرنیٹ"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"نامعلوم نیٹ ورک کی قسم"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-uz/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-uz/strings.xml
index 7518db3..567aa88 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-uz/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-uz/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Wi-Fi tarmoqqa kirish"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Tarmoqqa kirish"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda internetga ruxsati yoʻq"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Variantlarni ko‘rsatish uchun bosing"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mobil tarmoq internetga ulanmagan"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Tarmoq internetga ulanmagan"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Xususiy DNS server ishlamayapti"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda aloqa cheklangan"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Baribir ulash uchun bosing"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Yangi ulanish: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Agar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tarmoqda internet uzilsa, qurilma <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ga ulanadi. Sarflangan trafik uchun haq olinishi mumkin."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> tarmog‘idan <xliff:g id="NEW_NETWORK">%2$s</xliff:g> tarmog‘iga o‘tildi"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"noma’lum tarmoq turi"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Tizim aloqa resurslari"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Wi-Fi tarmoqqa kirish"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Tarmoqqa kirish"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda internetga ruxsati yoʻq"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Variantlarni ko‘rsatish uchun bosing"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mobil tarmoq internetga ulanmagan"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Tarmoq internetga ulanmagan"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Xususiy DNS server ishlamayapti"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nomli tarmoqda aloqa cheklangan"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Baribir ulash uchun bosing"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Yangi ulanish: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Agar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tarmoqda internet uzilsa, qurilma <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ga ulanadi. Sarflangan trafik uchun haq olinishi mumkin."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> tarmog‘idan <xliff:g id="NEW_NETWORK">%2$s</xliff:g> tarmog‘iga o‘tildi"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"mobil internet"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"mobil internet"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"nomaʼlum tarmoq turi"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-vi/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-vi/strings.xml
index d2284d4..590b388 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-vi/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-vi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Đăng nhập vào mạng Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Đăng nhập vào mạng"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> không có quyền truy cập Internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Nhấn để biết tùy chọn"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Mạng di động không có quyền truy cập Internet"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Mạng không có quyền truy cập Internet"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Không thể truy cập máy chủ DNS riêng tư"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> có khả năng kết nối giới hạn"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Nhấn để tiếp tục kết nối"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Đã chuyển sang <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Thiết bị sử dụng <xliff:g id="NEW_NETWORK">%1$s</xliff:g> khi <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> không có quyền truy cập Internet. Bạn có thể phải trả phí."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Đã chuyển từ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> sang <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"loại mạng không xác định"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Tài nguyên kết nối hệ thống"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Đăng nhập vào mạng Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Đăng nhập vào mạng"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> không có quyền truy cập Internet"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Nhấn để biết tùy chọn"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Mạng di động không có quyền truy cập Internet"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Mạng không có quyền truy cập Internet"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Không thể truy cập máy chủ DNS riêng tư"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> có khả năng kết nối giới hạn"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Nhấn để tiếp tục kết nối"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Đã chuyển sang <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Thiết bị sử dụng <xliff:g id="NEW_NETWORK">%1$s</xliff:g> khi <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> không có quyền truy cập Internet. Bạn có thể phải trả phí."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Đã chuyển từ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> sang <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"dữ liệu di động"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"Bluetooth"</item>
- <item msgid="1616528372438698248">"Ethernet"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"dữ liệu di động"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"Bluetooth"</item>
+ <item msgid="1160736166977503463">"Ethernet"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"loại mạng không xác định"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml
index 813482b..9d6cff9 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rCN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"登录到WLAN网络"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"登录到网络"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 无法访问互联网"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"点按即可查看相关选项"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"此移动网络无法访问互联网"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"此网络无法访问互联网"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"无法访问私人 DNS 服务器"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的连接受限"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"点按即可继续连接"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"已切换至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"设备会在<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>无法访问互联网时使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g>(可能需要支付相应的费用)。"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"已从<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切换至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"未知网络类型"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"系统网络连接资源"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"登录到WLAN网络"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"登录到网络"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 无法访问互联网"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"点按即可查看相关选项"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"此移动网络无法访问互联网"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"此网络无法访问互联网"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"无法访问私人 DNS 服务器"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的连接受限"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"点按即可继续连接"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"已切换至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"设备会在<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>无法访问互联网时使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g>(可能需要支付相应的费用)。"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"已从<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切换至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"移动数据"</item>
- <item msgid="5520925862115353992">"WLAN"</item>
- <item msgid="1055487873974272842">"蓝牙"</item>
- <item msgid="1616528372438698248">"以太网"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"移动数据"</item>
+ <item msgid="6341719431034774569">"WLAN"</item>
+ <item msgid="5081440868800877512">"蓝牙"</item>
+ <item msgid="1160736166977503463">"以太网"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"未知网络类型"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml
index 676404f..c84241c 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rHK/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"登入 Wi-Fi 網絡"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"登入網絡"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>未有連接至互聯網"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"輕按即可查看選項"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"流動網絡並未連接互聯網"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"網絡並未連接互聯網"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"無法存取私人 DNS 伺服器"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>連線受限"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"仍要輕按以連結至此網絡"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"裝置會在 <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> 無法連線至互聯網時使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g> (可能需要支付相關費用)。"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"已從<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"不明網絡類型"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"系統連線資源"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"登入 Wi-Fi 網絡"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"登入網絡"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>未有連接至互聯網"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"輕按即可查看選項"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"流動網絡並未連接互聯網"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"網絡並未連接互聯網"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"無法存取私人 DNS 伺服器"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g>連線受限"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"仍要輕按以連結至此網絡"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"裝置會在 <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> 無法連線至互聯網時使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g> (可能需要支付相關費用)。"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"已從<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"流動數據"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"藍牙"</item>
- <item msgid="1616528372438698248">"以太網"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"流動數據"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"藍牙"</item>
+ <item msgid="1160736166977503463">"以太網絡"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"不明網絡類型"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml
index f355138..07540d1 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zh-rTW/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"登入 Wi-Fi 網路"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"登入網路"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 沒有網際網路連線"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"輕觸即可查看選項"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"這個行動網路沒有網際網路連線"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"這個網路沒有網際網路連線"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"無法存取私人 DNS 伺服器"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的連線能力受限"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"輕觸即可繼續連線"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"裝置會在無法連上「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」時切換至「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」(可能需要支付相關費用)。"</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"已從 <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> 切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"不明的網路類型"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"系統連線資源"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"登入 Wi-Fi 網路"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"登入網路"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 沒有網際網路連線"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"輕觸即可查看選項"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"這個行動網路沒有網際網路連線"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"這個網路沒有網際網路連線"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"無法存取私人 DNS 伺服器"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> 的連線能力受限"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"輕觸即可繼續連線"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"裝置會在無法連上「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」時切換至「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」(可能需要支付相關費用)。"</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"已從 <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> 切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"行動數據"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
- <item msgid="1055487873974272842">"藍牙"</item>
- <item msgid="1616528372438698248">"乙太網路"</item>
- <item msgid="9177085807664964627">"VPN"</item>
+ <item msgid="5454013645032700715">"行動數據"</item>
+ <item msgid="6341719431034774569">"Wi-Fi"</item>
+ <item msgid="5081440868800877512">"藍牙"</item>
+ <item msgid="1160736166977503463">"乙太網路"</item>
+ <item msgid="7347618872551558605">"VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"不明的網路類型"</string>
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zu/strings.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zu/strings.xml
index 55fefb7..19f390b 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values-zu/strings.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-zu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,27 +13,31 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Ngena ngemvume kunethiwekhi ye-Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Ngena ngemvume kunethiwekhi"</string>
- <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
- <string name="wifi_no_internet" msgid="1386911698276448061">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ayinakho ukufinyelela kwe-inthanethi"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Thepha ukuze uthole izinketho"</string>
- <string name="mobile_no_internet" msgid="4014455157529909781">"Inethiwekhi yeselula ayinakho ukufinyelela kwe-inthanethi"</string>
- <string name="other_networks_no_internet" msgid="6698711684200067033">"Inethiwekhi ayinakho ukufinyelela kwenethiwekhi"</string>
- <string name="private_dns_broken_detailed" msgid="3709388271074611847">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
- <string name="network_partial_connectivity" msgid="4791024923851432291">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> inokuxhumeka okukhawulelwe"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"Thepha ukuze uxhume noma kunjalo"</string>
- <string name="network_switch_metered" msgid="1531869544142283384">"Kushintshelwe ku-<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
- <string name="network_switch_metered_detail" msgid="1358296010128405906">"Idivayisi isebenzisa i-<xliff:g id="NEW_NETWORK">%1$s</xliff:g> uma i-<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> inganakho ukufinyelela kwe-inthanethi. Kungasebenza izindleko."</string>
- <string name="network_switch_metered_toast" msgid="501662047275723743">"Kushintshelewe kusuka ku-<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kuya ku-<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
- <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"uhlobo olungaziwa lwenethiwekhi"</string>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="connectivityResourcesAppLabel" msgid="8294935652079168395">"Izinsiza Zokuxhumeka Zesistimu"</string>
+ <string name="wifi_available_sign_in" msgid="5254156478006453593">"Ngena ngemvume kunethiwekhi ye-Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="7794369329839408792">"Ngena ngemvume kunethiwekhi"</string>
+ <!-- no translation found for network_available_sign_in_detailed (3643910593681893097) -->
+ <skip />
+ <string name="wifi_no_internet" msgid="3961697321010262514">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> ayinakho ukufinyelela kwe-inthanethi"</string>
+ <string name="wifi_no_internet_detailed" msgid="1229067002306296104">"Thepha ukuze uthole izinketho"</string>
+ <string name="mobile_no_internet" msgid="2262524005014119639">"Inethiwekhi yeselula ayinakho ukufinyelela kwe-inthanethi"</string>
+ <string name="other_networks_no_internet" msgid="8226004998719563755">"Inethiwekhi ayinakho ukufinyelela kwenethiwekhi"</string>
+ <string name="private_dns_broken_detailed" msgid="3537567373166991809">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
+ <string name="network_partial_connectivity" msgid="5957065286265771273">"I-<xliff:g id="NETWORK_SSID">%1$s</xliff:g> inokuxhumeka okukhawulelwe"</string>
+ <string name="network_partial_connectivity_detailed" msgid="6975752539442533034">"Thepha ukuze uxhume noma kunjalo"</string>
+ <string name="network_switch_metered" msgid="2814798852883117872">"Kushintshelwe ku-<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
+ <string name="network_switch_metered_detail" msgid="605546931076348229">"Idivayisi isebenzisa i-<xliff:g id="NEW_NETWORK">%1$s</xliff:g> uma i-<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> inganakho ukufinyelela kwe-inthanethi. Kungasebenza izindleko."</string>
+ <string name="network_switch_metered_toast" msgid="8831325515040986641">"Kushintshelewe kusuka ku-<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kuya ku-<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"idatha yeselula"</item>
- <item msgid="5520925862115353992">"I-Wi-Fi"</item>
- <item msgid="1055487873974272842">"I-Bluetooth"</item>
- <item msgid="1616528372438698248">"I-Ethernet"</item>
- <item msgid="9177085807664964627">"I-VPN"</item>
+ <item msgid="5454013645032700715">"idatha yeselula"</item>
+ <item msgid="6341719431034774569">"I-Wi-Fi"</item>
+ <item msgid="5081440868800877512">"I-Bluetooth"</item>
+ <item msgid="1160736166977503463">"I-Ethernet"</item>
+ <item msgid="7347618872551558605">"I-VPN"</item>
</string-array>
+ <string name="network_switch_type_name_unknown" msgid="7826330274368951740">"uhlobo olungaziwa lwenethiwekhi"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 087275e..801b490 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
+import android.content.AttributionSource;
import android.content.ContentResolver;
import android.content.UriPermission;
import android.database.Cursor;
@@ -28,7 +29,6 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
-import android.os.IBinder;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.DiskInfo;
@@ -141,17 +141,17 @@
}
@Override
- protected int enforceReadPermissionInner(Uri uri, String callingPkg,
- @Nullable String featureId, IBinder callerToken) throws SecurityException {
+ protected int enforceReadPermissionInner(Uri uri,
+ @NonNull AttributionSource attributionSource) throws SecurityException {
enforceShellRestrictions();
- return super.enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
+ return super.enforceReadPermissionInner(uri, attributionSource);
}
@Override
- protected int enforceWritePermissionInner(Uri uri, String callingPkg,
- @Nullable String featureId, IBinder callerToken) throws SecurityException {
+ protected int enforceWritePermissionInner(Uri uri,
+ @NonNull AttributionSource attributionSource) throws SecurityException {
enforceShellRestrictions();
- return super.enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
+ return super.enforceWritePermissionInner(uri, attributionSource);
}
public void updateVolumes() {
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
index 7cabdd5..5b17d74 100644
--- a/packages/PackageInstaller/res/values-iw/strings.xml
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -21,9 +21,9 @@
<string name="done" msgid="6632441120016885253">"סיום"</string>
<string name="cancel" msgid="1018267193425558088">"ביטול"</string>
<string name="installing" msgid="4921993079741206516">"מתקין…"</string>
- <string name="installing_app" msgid="1165095864863849422">"מתקין את <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
+ <string name="installing_app" msgid="1165095864863849422">"מתבצעת התקנה של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
<string name="install_done" msgid="5987363587661783896">"האפליקציה הותקנה."</string>
- <string name="install_confirm_question" msgid="8176284075816604590">"האם ברצונך להתקין אפליקציה זו?"</string>
+ <string name="install_confirm_question" msgid="8176284075816604590">"להתקין את האפליקציה הזו?"</string>
<string name="install_confirm_question_update" msgid="7942235418781274635">"האם ברצונך להתקין עדכון עבור אפליקציה קיימת זו? הנתונים הקיימים שלך לא יאבדו."</string>
<string name="install_confirm_question_update_system" msgid="4713001702777910263">"האם ברצונך להתקין עדכון עבור אפליקציה מובנית זו? הנתונים הקיימים שלך לא יאבדו."</string>
<string name="install_failed" msgid="5777824004474125469">"האפליקציה לא הותקנה."</string>
@@ -31,14 +31,14 @@
<string name="install_failed_conflict" msgid="3493184212162521426">"האפליקציה לא הותקנה כי החבילה מתנגשת עם חבילה קיימת."</string>
<string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"האפליקציה לא הותקנה כי האפליקציה אינה תואמת לטאבלט."</string>
<string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"האפליקציה הזו אינה תואמת לטלוויזיה שלך."</string>
- <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"האפליקציה לא הותקנה כי האפליקציה אינה תואמת לטלפון."</string>
+ <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"האפליקציה לא הותקנה כי היא לא תואמת לטלפון."</string>
<string name="install_failed_invalid_apk" msgid="8581007676422623930">"האפליקציה לא הותקנה כי נראה שהחבילה לא תקפה."</string>
<string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"לא ניתן להתקין את <xliff:g id="APP_NAME">%1$s</xliff:g> בטאבלט שלך."</string>
<string name="install_failed_msg" product="tv" msgid="1920009940048975221">"לא ניתן להתקין את <xliff:g id="APP_NAME">%1$s</xliff:g> בטלוויזיה שלך."</string>
<string name="install_failed_msg" product="default" msgid="6484461562647915707">"לא ניתן להתקין את <xliff:g id="APP_NAME">%1$s</xliff:g> בטלפון שלך."</string>
<string name="launch" msgid="3952550563999890101">"פתיחה"</string>
<string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"מנהל המערכת שלך לא מתיר התקנה של אפליקציות ממקורות לא ידועים"</string>
- <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"למשתמש זה אין הרשאה להתקין אפליקציות שאינן מוכרות"</string>
+ <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"למשתמש הזה אין הרשאה להתקין אפליקציות שאינן מוכרות"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"למשתמש הזה אין הרשאה להתקין אפליקציות"</string>
<string name="ok" msgid="7871959885003339302">"אישור"</string>
<string name="manage_applications" msgid="5400164782453975580">"ניהול אפליקציות"</string>
@@ -52,16 +52,16 @@
<string name="generic_error_dlg_text" msgid="5287861443265795232">"לא ניתן היה להסיר את התקנת האפליקציה."</string>
<string name="uninstall_application_title" msgid="4045420072401428123">"הסרת התקנה של האפליקציה"</string>
<string name="uninstall_update_title" msgid="824411791011583031">"הסרת התקנה של עדכון"</string>
- <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> הוא חלק מהאפליקציה הבאה:"</string>
+ <string name="uninstall_activity_text" msgid="1928194674397770771">"הפעילות <xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> היא חלק מהאפליקציה הבאה:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"האם ברצונך להסיר את ההתקנה של אפליקציה זו?"</string>
- <string name="uninstall_application_text_all_users" msgid="575491774380227119">"האם אתה רוצה להסיר את האפליקציה הזו עבור "<b>"כל"</b>" המשתמשים? האפליקציה והנתונים שלה יוסרו מ"<b>"כל"</b>" המשתמשים במכשיר."</string>
- <string name="uninstall_application_text_user" msgid="498072714173920526">"האם ברצונך להסיר את התקנתה של אפליקציה זו עבור המשתמש <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"להסיר את האפליקציה הזו עבור "<b>"כל"</b>" המשתמשים? האפליקציה והנתונים שלה יוסרו עבור "<b>"כל"</b>" המשתמשים במכשיר."</string>
+ <string name="uninstall_application_text_user" msgid="498072714173920526">"להסיר את ההתקנה של האפליקציה הזו עבור <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_update_text" msgid="863648314632448705">"האם להחליף את האפליקציה הזאת בגרסת היצרן? כל הנתונים יוסרו."</string>
<string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"האם להחליף את האפליקציה הזאת בגרסת היצרן? כל הנתונים יוסרו. הפעולה תשפיע על כל משתמשי המכשיר, כולל משתמשים בעלי פרופיל עבודה."</string>
<string name="uninstall_keep_data" msgid="7002379587465487550">"שמירת <xliff:g id="SIZE">%1$s</xliff:g> מנתוני האפליקציה."</string>
<string name="uninstalling_notification_channel" msgid="840153394325714653">"התקנות בתהליכי הסרה"</string>
<string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"הסרות התקנה שנכשלו"</string>
- <string name="uninstalling" msgid="8709566347688966845">"מסיר התקנה..."</string>
+ <string name="uninstalling" msgid="8709566347688966845">"בתהליך הסרת התקנה..."</string>
<string name="uninstalling_app" msgid="8866082646836981397">"מסיר את ההתקנה של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
<string name="uninstall_done" msgid="439354138387969269">"הסרת ההתקנה הסתיימה."</string>
<string name="uninstall_done_app" msgid="4588850984473605768">"ההתקנה של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> הוסרה"</string>
diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml
index d0902c3..28ad6aa 100644
--- a/packages/PackageInstaller/res/values-sv/strings.xml
+++ b/packages/PackageInstaller/res/values-sv/strings.xml
@@ -24,8 +24,8 @@
<string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> installeras …"</string>
<string name="install_done" msgid="5987363587661783896">"Appen har installerats."</string>
<string name="install_confirm_question" msgid="8176284075816604590">"Vill du installera det här programmet?"</string>
- <string name="install_confirm_question_update" msgid="7942235418781274635">"Vill du installera en uppdatering till den här befintliga appen? Dina befintliga data försvinner inte."</string>
- <string name="install_confirm_question_update_system" msgid="4713001702777910263">"Vill du installera en uppdatering av den inbyggda appen? Dina befintliga data försvinner inte."</string>
+ <string name="install_confirm_question_update" msgid="7942235418781274635">"Vill du installera en uppdatering till den här befintliga appen? Din befintliga data försvinner inte."</string>
+ <string name="install_confirm_question_update_system" msgid="4713001702777910263">"Vill du installera en uppdatering av den inbyggda appen? Din befintliga data försvinner inte."</string>
<string name="install_failed" msgid="5777824004474125469">"Appen har inte installerats."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketet har blockerats för installation."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Appen har inte installerats på grund av en konflikt mellan detta paket och ett befintligt paket."</string>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index 64db711..5dcd7f2 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -32,10 +32,10 @@
<string name="template_page_range" msgid="428638530038286328">"טווח של <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"למשל 1–5,8,11–13"</string>
<string name="print_preview" msgid="8010217796057763343">"תצוגה מקדימה של הדפסה"</string>
- <string name="install_for_print_preview" msgid="6366303997385509332">"התקן מציג PDF ליצירת תצוגה מקדימה"</string>
+ <string name="install_for_print_preview" msgid="6366303997385509332">"התקנה של PDF viewer ליצירת תצוגה מקדימה"</string>
<string name="printing_app_crashed" msgid="854477616686566398">"אפליקציית ההדפסה קרסה"</string>
<string name="generating_print_job" msgid="3119608742651698916">"יוצר עבודת הדפסה"</string>
- <string name="save_as_pdf" msgid="5718454119847596853">"שמור כ-PDF"</string>
+ <string name="save_as_pdf" msgid="5718454119847596853">"שמירה כ-PDF"</string>
<string name="all_printers" msgid="5018829726861876202">"כל המדפסות…"</string>
<string name="print_dialog" msgid="32628687461331979">"תיבת דו שיח של מדפסת"</string>
<string name="current_page_template" msgid="5145005201131935302">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
@@ -44,7 +44,7 @@
<string name="expand_handle" msgid="7282974448109280522">"ידית הרחבה"</string>
<string name="collapse_handle" msgid="6886637989442507451">"ידית כיווץ"</string>
<string name="print_button" msgid="645164566271246268">"הדפס"</string>
- <string name="savetopdf_button" msgid="2976186791686924743">"שמור כ-PDF"</string>
+ <string name="savetopdf_button" msgid="2976186791686924743">"שמירה כ-PDF"</string>
<string name="print_options_expanded" msgid="6944679157471691859">"אפשרויות ההדפסה הורחבו"</string>
<string name="print_options_collapsed" msgid="7455930445670414332">"אפשרויות ההדפסה כווצו"</string>
<string name="search" msgid="5421724265322228497">"חיפוש"</string>
@@ -91,7 +91,7 @@
<string name="restart" msgid="2472034227037808749">"הפעלה מחדש"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"אין חיבור למדפסת"</string>
<string name="reason_unknown" msgid="5507940196503246139">"לא ידוע"</string>
- <string name="print_service_security_warning_title" msgid="2160752291246775320">"האם להשתמש ב-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"האם להשתמש בשירות <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ייתכן שהמסמך שלך יעבור בשרת אחד או יותר בדרכו למדפסת."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"שחור ולבן"</item>
@@ -107,7 +107,7 @@
<item msgid="3199660090246166812">"לרוחב"</item>
</string-array>
<string name="print_write_error_message" msgid="5787642615179572543">"לא ניתן היה לכתוב לקובץ"</string>
- <string name="print_error_default_message" msgid="8602678405502922346">"מצטערים, אך זה לא עבד. נסה שוב."</string>
+ <string name="print_error_default_message" msgid="8602678405502922346">"מצטערים, הפעולה לא בוצעה. אפשר לנסות שוב."</string>
<string name="print_error_retry" msgid="1426421728784259538">"כדאי לנסות שוב"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"המדפסת הזו אינה זמינה כעת."</string>
<string name="print_cannot_load_page" msgid="6179560924492912009">"לא ניתן להציג תצוגה מקדימה"</string>
diff --git a/packages/PrintSpooler/res/values-nl/strings.xml b/packages/PrintSpooler/res/values-nl/strings.xml
index 6448acc..2cd8d9b 100644
--- a/packages/PrintSpooler/res/values-nl/strings.xml
+++ b/packages/PrintSpooler/res/values-nl/strings.xml
@@ -50,7 +50,7 @@
<string name="search" msgid="5421724265322228497">"Zoeken"</string>
<string name="all_printers_label" msgid="3178848870161526399">"Alle printers"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"Service toevoegen"</string>
- <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Zoekvak weergegeven"</string>
+ <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Zoekvak wordt getoond"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Zoekvak verborgen"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Printer toevoegen"</string>
<string name="print_select_printer" msgid="7388760939873368698">"Printer selecteren"</string>
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 8d9a562..ae9261c 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -63,7 +63,8 @@
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- LayoutInflater.from(context).inflate(R.layout.main_switch_bar, this);
+ LayoutInflater.from(context).inflate(resourceId(context, "layout", "main_switch_bar"),
+ this);
setFocusable(true);
setClickable(true);
@@ -255,4 +256,8 @@
requestLayout();
}
+
+ private int resourceId(Context context, String type, String name) {
+ return context.getResources().getIdentifier(name, type, context.getPackageName());
+ }
}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index 49f6bd8..a2bec33 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -555,7 +555,7 @@
bundle.putString(META_DATA_PREFERENCE_KEYHINT, key);
}
try {
- return provider.call(context.getPackageName(), context.getAttributionTag(),
+ return provider.call(context.getAttributionSource(),
uri.getAuthority(), method, uri.toString(), bundle);
} catch (RemoteException e) {
return null;
diff --git a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
index f45105d..31b3fe5 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
+++ b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
@@ -70,4 +70,13 @@
android:scaleY="2"
android:layout_marginTop="4dp"
android:max="100"/>
+
+ <TextView
+ android:id="@+id/bottom_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:ellipsize="marquee"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
+ android:textSize="14sp"/>
</LinearLayout>
diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
index 2185950..a2b1de2 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
+++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
@@ -44,6 +44,7 @@
private CharSequence mUsageSummary;
private CharSequence mTotalSummary;
+ private CharSequence mBottomSummary;
private ImageView mCustomImageView;
private int mPercent = -1;
@@ -101,6 +102,15 @@
notifyChanged();
}
+ /** Set bottom summary. */
+ public void setBottomSummary(CharSequence bottomSummary) {
+ if (TextUtils.equals(mBottomSummary, bottomSummary)) {
+ return;
+ }
+ mBottomSummary = bottomSummary;
+ notifyChanged();
+ }
+
/** Set percentage of the progress bar. */
public void setPercent(long usage, long total) {
if (total == 0L || usage > total) {
@@ -147,6 +157,14 @@
totalSummary.setText(mTotalSummary);
}
+ final TextView bottomSummary = (TextView) holder.findViewById(R.id.bottom_summary);
+ if (TextUtils.isEmpty(mBottomSummary)) {
+ bottomSummary.setVisibility(View.GONE);
+ } else {
+ bottomSummary.setVisibility(View.VISIBLE);
+ bottomSummary.setText(mBottomSummary);
+ }
+
final ProgressBar progressBar = (ProgressBar) holder.findViewById(android.R.id.progress);
if (mPercent < 0) {
progressBar.setIndeterminate(true);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7186ec5..927e9db 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1272,6 +1272,22 @@
<!-- Button label for generic OK action [CHAR LIMIT=20] -->
<string name="okay">OK</string>
+ <!-- Label for the settings activity for controlling apps that can schedule alarms [CHAR LIMIT=30] -->
+ <string name="alarms_and_reminders_label">Alarms and reminders</string>
+ <!-- Label for the switch to toggler the permission for scheduling alarms [CHAR LIMIT=50] -->
+ <string name="alarms_and_reminders_switch_title">Allow to set alarms or reminders</string>
+ <!-- Title for the setting screen for controlling apps that can schedule alarms [CHAR LIMIT=30] -->
+ <string name="alarms_and_reminders_title">Alarms and reminders</string>
+ <!-- Description that appears below the alarms_and_reminders switch [CHAR LIMIT=NONE] -->
+ <string name="alarms_and_reminders_footer_title">
+ Allow this app to schedule alarms or other timing based events.
+ This will allow the app to wake up and run even when you are not using the device.
+ Note that revoking this permission may cause the app to malfunction, specifically any alarms
+ that the app has scheduled will no longer work.
+ </string>
+ <!-- Keywords for setting screen for controlling apps that can schedule alarms [CHAR LIMIT=100] -->
+ <string name="keywords_alarms_and_reminders">schedule, alarm, reminder, event</string>
+
<!-- Do not disturb: Label for button in enable zen dialog that will turn on zen mode. [CHAR LIMIT=30] -->
<string name="zen_mode_enable_dialog_turn_on">Turn on</string>
<!-- Do not disturb: Title for the Do not Disturb dialog to turn on Do not disturb. [CHAR LIMIT=50]-->
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 8987968..a5da8b6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Log;
import com.android.settingslib.R;
@@ -198,4 +199,17 @@
}
return false;
}
+
+ /**
+ * Returns a boolean indicating whether a given package is a default browser.
+ *
+ * @param packageName a given package.
+ * @return true if the given package is default browser.
+ */
+ public static boolean isDefaultBrowser(Context context, String packageName) {
+ final String defaultBrowserPackage =
+ context.getPackageManager().getDefaultBrowserPackageNameAsUser(
+ UserHandle.myUserId());
+ return TextUtils.equals(packageName, defaultBrowserPackage);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 8f8f859..253629c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -13,11 +13,14 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.provider.DeviceConfig;
import android.provider.MediaStore;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
import androidx.core.graphics.drawable.IconCompat;
import com.android.settingslib.R;
@@ -34,6 +37,7 @@
public static final boolean D = true; // regular logging
public static final int META_INT_ERROR = -1;
+ public static final String BT_ADVANCED_HEADER_ENABLED = "bt_advanced_header_enabled";
private static ErrorListener sErrorListener;
@@ -178,14 +182,12 @@
final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
context, cachedDevice);
final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
- final boolean untetheredHeadset = getBooleanMetaData(
- bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET);
final int iconSize = context.getResources().getDimensionPixelSize(
R.dimen.bt_nearby_icon_size);
final Resources resources = context.getResources();
- // Deal with untethered headset
- if (untetheredHeadset) {
+ // Deal with advanced device icon
+ if (isAdvancedDetailsHeader(bluetoothDevice)) {
final Uri iconUri = getUriMetaData(bluetoothDevice,
BluetoothDevice.METADATA_MAIN_ICON);
if (iconUri != null) {
@@ -217,6 +219,35 @@
}
/**
+ * Check if the Bluetooth device supports advanced metadata
+ *
+ * @param bluetoothDevice the BluetoothDevice to get metadata
+ * @return true if it supports advanced metadata, false otherwise.
+ */
+ public static boolean isAdvancedDetailsHeader(@NonNull BluetoothDevice bluetoothDevice) {
+ if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, BT_ADVANCED_HEADER_ENABLED,
+ true)) {
+ Log.d(TAG, "isAdvancedDetailsHeader: advancedEnabled is false");
+ return false;
+ }
+ // The metadata is for Android R
+ if (getBooleanMetaData(bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) {
+ Log.d(TAG, "isAdvancedDetailsHeader: untetheredHeadset is true");
+ return true;
+ }
+ // The metadata is for Android S
+ String deviceType = getStringMetaData(bluetoothDevice,
+ BluetoothDevice.METADATA_DEVICE_TYPE);
+ if (TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET)
+ || TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_WATCH)
+ || TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_DEFAULT)) {
+ Log.d(TAG, "isAdvancedDetailsHeader: deviceType is " + deviceType);
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Create an Icon pointing to a drawable.
*/
public static IconCompat createIconWithDrawable(Drawable drawable) {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
index fe76b06..cd78add 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
@@ -92,6 +92,28 @@
}
@Test
+ public void setBottomSummary_getCorrectSummary() {
+ final String expectedText = "Should last until about 7:45 PM";
+ mUsageProgressBarPreference.setBottomSummary(expectedText);
+
+ mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+ final TextView bottomSummary = (TextView) mViewHolder.findViewById(R.id.bottom_summary);
+ assertThat(bottomSummary.getText()).isEqualTo(expectedText);
+ assertThat(bottomSummary.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void setBottomSummary_emptyText_isGone() {
+ mUsageProgressBarPreference.setBottomSummary(null);
+
+ mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+ final TextView bottomSummary = (TextView) mViewHolder.findViewById(R.id.bottom_summary);
+ assertThat(bottomSummary.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
public void setPercent_getCorrectProgress() {
mUsageProgressBarPreference.setPercent(31, 80);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 3a95852c..2e85514 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -150,4 +150,45 @@
assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice,
BluetoothDevice.METADATA_MAIN_ICON)).isNull();
}
+
+ @Test
+ public void isAdvancedDetailsHeader_untetheredHeadset_returnTrue() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+ BOOL_METADATA.getBytes());
+
+ assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
+ }
+
+ @Test
+ public void isAdvancedDetailsHeader_deviceTypeUntetheredHeadset_returnTrue() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
+
+ assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
+ }
+
+ @Test
+ public void isAdvancedDetailsHeader_deviceTypeWatch_returnTrue() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
+
+ assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
+ }
+
+ @Test
+ public void isAdvancedDetailsHeader_deviceTypeDefault_returnTrue() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_DEFAULT.getBytes());
+
+ assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(true);
+ }
+
+ @Test
+ public void isAdvancedDetailsHeader_noMetadata_returnFalse() {
+ assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(false);
+ }
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index 987e82e..77032ba 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -72,6 +72,8 @@
Settings.Global.NOTIFICATION_BUBBLES,
Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP,
Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER,
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
+ Settings.Global.USER_DISABLED_HDR_FORMATS,
+ Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueIntegerListValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueIntegerListValidator.java
new file mode 100644
index 0000000..eb38ac5
--- /dev/null
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueIntegerListValidator.java
@@ -0,0 +1,50 @@
+/*
+ * 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.provider.settings.validators;
+
+/**
+ * Validates the elements in a list with the array of allowed integer values.
+ *
+ * @hide
+ */
+class DiscreteValueIntegerListValidator extends ListValidator {
+ private int[] mAllowedValues;
+
+ DiscreteValueIntegerListValidator(String listSplitRegex, int[] allowedValues) {
+ super(listSplitRegex);
+ mAllowedValues = allowedValues;
+ }
+
+ @Override
+ protected boolean isEntryValid(String entry) {
+ return (entry != null);
+ }
+
+ @Override
+ protected boolean isItemValid(String item) {
+ for (int allowedValue : mAllowedValues) {
+ try {
+ if (Integer.parseInt(item) == allowedValue) {
+ return true;
+ }
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index ad6a531..0790189 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -16,16 +16,16 @@
package android.provider.settings.validators;
+import static android.media.AudioFormat.SURROUND_SOUND_ENCODING;
import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.PERCENTAGE_INTEGER_VALIDATOR;
+import static android.view.Display.HdrCapabilities.HDR_TYPES;
-import android.media.AudioFormat;
import android.os.BatteryManager;
import android.provider.Settings.Global;
-import android.text.TextUtils;
import android.util.ArrayMap;
import java.util.Map;
@@ -88,31 +88,17 @@
Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS,
new DiscreteValueValidator(new String[] {"0", "1"}));
VALIDATORS.put(
+ Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED,
+ new DiscreteValueValidator(new String[] {"0", "1"}));
+ VALIDATORS.put(
+ Global.USER_DISABLED_HDR_FORMATS,
+ new DiscreteValueIntegerListValidator(",", HDR_TYPES));
+ VALIDATORS.put(
Global.ENCODED_SURROUND_OUTPUT,
new DiscreteValueValidator(new String[] {"0", "1", "2", "3"}));
VALIDATORS.put(
Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS,
- value -> {
- try {
- String[] surroundFormats = TextUtils.split(value, ",");
- for (String format : surroundFormats) {
- int audioFormat = Integer.valueOf(format);
- boolean isSurroundFormat = false;
- for (int sf : AudioFormat.SURROUND_SOUND_ENCODING) {
- if (sf == audioFormat) {
- isSurroundFormat = true;
- break;
- }
- }
- if (!isSurroundFormat) {
- return false;
- }
- }
- return true;
- } catch (NumberFormatException e) {
- return false;
- }
- });
+ new DiscreteValueIntegerListValidator(",", SURROUND_SOUND_ENCODING));
VALIDATORS.put(
Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL,
new InclusiveIntegerRangeValidator(0, 100));
@@ -154,3 +140,4 @@
/* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
}
}
+
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index db9b83e..53920f0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -165,6 +165,8 @@
VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ASSIST_TOUCH_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ASSIST_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"}));
VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index fdbbc39..df6ff73 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.app.ActivityManager;
+import android.content.AttributionSource;
import android.content.IContentProvider;
import android.os.Binder;
import android.os.Bundle;
@@ -250,7 +251,8 @@
Bundle args = new Bundle();
args.putInt(Settings.CALL_METHOD_USER_KEY,
ActivityManager.getService().getCurrentUser().id);
- Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ Bundle b = provider.call(new AttributionSource(Process.myUid(),
+ resolveCallingPackage(), null), Settings.AUTHORITY,
Settings.CALL_METHOD_DELETE_CONFIG, compositeKey, args);
success = (b != null && b.getInt(SettingsProvider.RESULT_ROWS_DELETED) == 1);
} catch (RemoteException e) {
@@ -266,7 +268,8 @@
Bundle args = new Bundle();
args.putInt(Settings.CALL_METHOD_USER_KEY,
ActivityManager.getService().getCurrentUser().id);
- Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ Bundle b = provider.call(new AttributionSource(Process.myUid(),
+ resolveCallingPackage(), null), Settings.AUTHORITY,
Settings.CALL_METHOD_LIST_CONFIG, null, args);
if (b != null) {
Map<String, String> flagsToValues =
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 7288371..4119dc9f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1881,6 +1881,12 @@
dumpSetting(s, p,
Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED,
+ SecureSettingsProto.Assist.TOUCH_GESTURE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED,
+ SecureSettingsProto.Assist.LONG_PRESS_HOME_ENABLED);
p.end(assistToken);
final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES);
@@ -2150,6 +2156,9 @@
dumpSetting(s, p,
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
SecureSettingsProto.InputMethods.SHOW_IME_WITH_HARD_KEYBOARD);
+ dumpSetting(s, p,
+ Settings.Secure.DEFAULT_VOICE_INPUT_METHOD,
+ SecureSettingsProto.InputMethods.DEFAULT_VOICE_INPUT_METHOD);
p.end(inputMethodsToken);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 3b3ca5b..17ebf6f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -17,6 +17,7 @@
package com.android.providers.settings;
import android.app.ActivityManager;
+import android.content.AttributionSource;
import android.content.IContentProvider;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -309,7 +310,9 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null);
+ Bundle result = provider.call(attributionSource, Settings.AUTHORITY,
callListCommand, null, arg);
lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST));
Collections.sort(lines);
@@ -334,7 +337,9 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null);
+ Bundle b = provider.call(attributionSource, Settings.AUTHORITY,
callGetCommand, key, arg);
if (b != null) {
result = b.getPairValue();
@@ -372,7 +377,9 @@
if (makeDefault) {
arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
- provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null);
+ provider.call(attributionSource, Settings.AUTHORITY,
callPutCommand, key, arg);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
@@ -396,7 +403,9 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null);
+ Bundle result = provider.call(attributionSource, Settings.AUTHORITY,
callDeleteCommand, key, arg);
return result.getInt(SettingsProvider.RESULT_ROWS_DELETED);
} catch (RemoteException e) {
@@ -423,7 +432,9 @@
}
String packageName = mPackageName != null ? mPackageName : resolveCallingPackage();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- provider.call(packageName, null, Settings.AUTHORITY, callResetCommand, null, arg);
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), resolveCallingPackage(), /*attributionTag*/ null);
+ provider.call(attributionSource, Settings.AUTHORITY, callResetCommand, null, arg);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cff8ad1..4c56db4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -35,6 +35,8 @@
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
+ <uses-permission android:name="android.permission.READ_CALL_LOG" />
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
<uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -194,11 +196,14 @@
<uses-permission android:name="android.permission.MANAGE_CAMERA" />
<!-- Permissions needed to test system only camera devices -->
<uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.BACKGROUND_CAMERA" />
<uses-permission android:name="android.permission.SYSTEM_CAMERA" />
<!-- Permissions needed to test onCameraOpened/Closed callbacks -->
<uses-permission android:name="android.permission.CAMERA_OPEN_CLOSE_LISTENER" />
<!-- Permissions needed for CTS camera test: RecordingTest.java when assuming shell id -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.RECORD_BACKGROUND_AUDIO" />
+
<!-- Permission needed to enable/disable Bluetooth/Wifi -->
<uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
<uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
diff --git a/packages/SoundPicker/res/values-pa/strings.xml b/packages/SoundPicker/res/values-pa/strings.xml
index 2653c64..eb630c9 100644
--- a/packages/SoundPicker/res/values-pa/strings.xml
+++ b/packages/SoundPicker/res/values-pa/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="ringtone_default" msgid="798836092118824500">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਰਿੰਗਟੋਨ"</string>
- <string name="notification_sound_default" msgid="8133121186242636840">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੂਚਨਾ ਧੁਨੀ"</string>
+ <string name="notification_sound_default" msgid="8133121186242636840">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੂਚਨਾ ਧੁਨੀ"</string>
<string name="alarm_sound_default" msgid="4787646764557462649">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਅਲਾਰਮ ਧੁਨੀ"</string>
<string name="add_ringtone_text" msgid="6642389991738337529">"ਰਿੰਗਟੋਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="add_alarm_text" msgid="3545497316166999225">"ਅਲਾਰਮ ਸ਼ਾਮਲ ਕਰੋ"</string>
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index f884270..835471d 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -9,6 +9,7 @@
awickham@google.com
beverlyt@google.com
brockman@google.com
+ccassidy@google.com
cinek@google.com
cwren@google.com
dupin@google.com
@@ -19,10 +20,10 @@
hyunyoungs@google.com
jaggies@google.com
jamesoleary@google.com
+jdemeulenaere@google.com
jeffdq@google.com
jjaggi@google.com
jonmiranda@google.com
-joshmcgrath@google.com
joshtrask@google.com
juliacr@google.com
juliatuttle@google.com
@@ -37,7 +38,6 @@
mpietal@google.com
mrcasey@google.com
mrenouf@google.com
-nbenbernou@google.com
nesciosquid@google.com
ogunwale@google.com
peanutbutter@google.com
@@ -45,6 +45,7 @@
pixel@google.com
roosa@google.com
santie@google.com
+shanh@google.com
snoeberger@google.com
sreyasr@google.com
steell@google.com
@@ -59,6 +60,7 @@
vadimt@google.com
victortulias@google.com
winsonc@google.com
+yurilin@google.com
xuqiu@google.com
zakcohen@google.com
diff --git a/packages/SystemUI/docs/falsing.md b/packages/SystemUI/docs/falsing.md
new file mode 100644
index 0000000..e224ca8
--- /dev/null
+++ b/packages/SystemUI/docs/falsing.md
@@ -0,0 +1,240 @@
+# Falsing in SystemUI
+
+Phones are easily and often accidentally-activated in owners' pockets ("falsing" or "pocket
+dialing"). Because a phone's screen can be turned on with a single tap, and because we have further
+actions that be activated with basic tapping and swiping, it is critical that we
+analyze touch events on the screen for intentional vs accidental behavior. With analysis,
+features within SystemUI have an opportunity to ignore or even undo accidental interactions as they
+are occurring.
+
+## Technical Details
+
+The `FalsingManager` tracks all touch interactions happening on a phone's lock screen.
+
+If you support any sort of touch gestures on the lock screen, you **must**, at a
+minimum, inform the `FalsingManager` of what touches are on touch targets vs not (things that may be
+ intentional). If you do not tell the `FalsingManager`, it will assume touches on your feature are
+always accidental and penalize the session accordingly.
+
+Individual touch targets do not _have_ to be separated out; it's acceptable to
+wrap your whole feature in one virtual block that reports touches to the
+`FalsingManager`, however more granular tracking will result in better results
+across the whole lock screen.
+
+You can _act_ on the results of the `FalsingManager`. Instead of only telling
+the `FalsingManager` that touch events were on touch targets, you can further use the
+returned results to decide if you want to respond to an owner's touch, if you
+want to prompt them to confirm their action, or if you simply want to ignore the
+touch.
+
+The flow through the system looks like such:
+
+1. Gesture on the screen.
+2. The `FalsingManager` makes a note of all of the `MotionEvents`.
+ * If no feature/touch target receives the `MotionEvents`, skip to 4.
+3. Your touch target receives the `MotionEvents`.
+ * Once your feature is ready to respond to the gesture in a substantive manner, it queries
+ the `FalsingManager`.
+ - Dragging animations, touch ripples, and other purely visual effects should not query.
+ - Query once you are ready to launch a new feature or dialogue, or are otherwise going to
+ change the state of the UI.
+ - Generally, wait until `MotionEvent.ACTION_UP` to query or `View.OnClickListener#onClick`.
+ - Only query once per gesture, at the end.
+ * If the `FalsingManager` says it looks good, respond to the touch.
+4. The `FalsingManager` checks to see if anyone queried about the gesture. If not, mark it as
+ accidental.
+
+There is also an event fired by the `FalsingManager` that can be listened to by anyone, that
+indicates that the the `FalsingManager` believes the phone is actively being pocket-dialed. When
+fired, modal features, such as quick settings, keyguard bouncer, and others should retract
+themselves to prevent further pocket-dialing.
+
+## Falsing "Belief" and History
+
+The `FalsingManager` maintains a recent history of false analyses. Using
+Bayesian statistics, it updates a "belief" in whether recent
+gestures are intentional or not. Any gesture that it is not explicitly queried about is treated as
+accidental, increasing the overall belief in
+false-iness. Gestures that are explicitly queried and that pass the relevant heuristics
+reduce belief that falsing is occurring. This information is tracked within the `HistoryTracker`.
+
+Changes in belief may influence internal heurstics within the `FalsingManager`,
+making it easier or harder for an owner to interact with their device. (An owner
+will always be able to interact with their device, but we may require double
+taps, or more deliberate swipes.)
+
+## Responding to Touch Events
+
+The methods below inform the `FalsingManager` that a tap is occurring within an expected touch
+target. Match the methods with the gesture you expect the device owner to use.
+
+### Single Tap
+
+`FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`. This
+method tells the `FalsingManager` that you want to validate a single tap. It
+returns true if it thinks the tap should be rejected (i.e. the tap looks more
+like a swipe) and false otherwise.
+
+`robustCheck` determines what heuristics are used. If set to false, the method
+performs a only very basic checking, checking that observed `MotionEvent`s are
+all within some small x & y region ("touch slop").
+
+When `robustCheck` is set to true, several more advanced rules are additionally
+applied:
+
+1. If the device recognizes a face (i.e. face-auth) the tap is **accepted**.
+2. If the tap is the _second_ tap in recent history and looks like a valid Double Tap
+ the tap is **accepted**. This works exactly like `FalsingManager#isFalseDoubleTap`.
+3. If the `HistoryTracker` reports strong belief in recent falsing, the tap is
+ **rejected**.
+4. Otherwise the tap is **accepted**.
+
+All the above rules are applied only after first confirming the gesture does
+in fact look like a basic tap.
+
+`falsePenalty` is a measure of how much the `HistoryTracker`'s belief should be
+penalized in the event that the tap is rejected. This value is only used if
+`robustCheck` is set to true.
+
+A value of `0` means no change in belief. A value of `1` means a _very_ strong
+confidence in a false tap. In general, as a single tap on the screen is not
+verifiable, a small value should be supplied - on the order of `0.1`. Pass `0`
+if you don't want to penalize belief at all. Pass a higher value
+the earlier in the UX flow your interaction occurs. Once an owner is farther
+along in a UX flow (multiple taps or swipes), its safer to assume that a single
+accidental tap should cause less of a penalty.
+
+### Double Tap
+
+`FalsingManager#isFalseDoubleTap()`. This method tells the `FalsingManager` that
+your UI wants to validate a double tap. There are no parameters to pass to this method.
+Call this when you explicitly receive and want to verify a double tap, _not_ a single tap.
+
+Note that `FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`
+will also check for double taps when `robustCheck` is set to true. If you are
+willing to use single taps, use that instead.
+
+### Swipes and Other Gestures
+
+`FalsingManager#isFalseTouch(@Classifier.InteractionType int interactionType)`.
+Use this for any non-tap interactions. This includes expanding notifications,
+expanding quick settings, pulling up the bouncer, and more. You must pass
+the type of interaction you are evaluating when calling it. A large set of
+heuristics will be applied to analyze the gesture, and the exact rules vary depending upon
+the `InteractionType`.
+
+### Ignoring A Gesture
+
+`FalsingCollector#avoidGesture()`. Tell the `FalsingManager` to pretend like the
+observed gesture never happened. **This method must be called when the observed
+`MotionEvent` is `MotionEvent.ACTION_DOWN`.** Attempting to call this method
+later in a gesture will not work.
+
+Notice that this method is actually a method on `FalsingCollector`. It is
+forcefully telling the `FalsingManager` to wholly pretend the gesture never
+happened. This is intended for security and PII sensitive gestures, such as
+password inputs. Please don't use this as a shortcut for avoiding the
+FalsingManager. Falsing works better the more behavior it is told about.
+
+### Other Considerations
+
+Please try to call the `FalsingManager` only once per gesture. Wait until you
+are ready to act on the owner's action, and then query the `FalsingManager`. The `FalsingManager`
+will update its belief in pocket dialing based only on the last call made, so multiple calls per
+gesture are not well defined.
+
+The `FalsingManager` does not update its belief in pocket-dialing until a new
+gesture starts. That is to say, if the owner makes a bad tap on your feature,
+the belief in pocket dialing will not incorporate this new data until the
+following gesture begins.
+
+If you expect a mix of taps, double taps, and swipes on your feature, segment them
+accordingly. Figure out which `FalsingManager` method you need to call first, rather than relying
+on multiple calls to the `FalsingManager` to act as a sieve.
+
+Don't:
+```
+if (!mFalsingManager.isFalseTap(false, 0)) {
+ // its a tap
+} else if (!mFalsingManager.isFalseTouch(GESTURE_A) {
+ // do thing a
+} else if (!mFalsingManager.isFalseTouch(GESTURE_B) {
+ // do thing b
+} else {
+ // must be a false.
+}
+```
+
+Do:
+```
+void onTap() {
+ if (!mFalsingManager.isFalseTap(false, 0)) {
+ // its a tap
+}
+
+void onGestureA() {
+ if (!mFalsingManager.isFalseTouch(GESTURE_A) {
+ // do thing a
+ }
+}
+
+void onGestureB() {
+ if (!mFalsingManager.isFalseTouch(GESTURE_B) {
+ // do thing b
+ }
+}
+```
+
+
+## Influencing Belief
+
+`FalsingCollector#updateFalseConfidence(FalsingClassifier.Result result)`. This
+method allows you to directly change the `FalsingManager`'s belief in the state
+of pocket dialing. If the owner does something unusual with their phone that you
+think indicates pocket dialing, you can call:
+
+```
+ mFalsingCollector.updateFalseConfidence(
+ FalsingClassifier.Result.falsed(0.6, "Owner is doing something fishy"));
+```
+
+A belief value of `1` indicates a 100% confidence of false behavior. A belief
+value of `0` would make no change in the `FalsingManager` and should be avoided
+as it simply creates noise in the logs. Generally, a middle value between the
+two extremes makes sense.
+
+A good example of where this is used is in the "Pattern" password input. We
+avoid recording those gestures in the `FalsingManager`, but we have the pattern input update
+the `FalsingManager` directly in some cases. If the owner simply taps on the pattern input, we
+record it as a false, (patterns are always 4 "cells" long, so single "cell" inputs are penalized).
+
+Conversely, if you think the owner does something that deserves a nice reward:
+
+```
+ mFalsingCollector.updateFalseConfidence(
+ FalsingClassifier.Result.passed(0.6));
+```
+
+Again, useful on password inputs where the FalsingManager is avoiding recording
+the gesture. This is used on the "pin" password input, to recognize successful
+taps on the input buttons.
+
+## Global Falsing Event
+
+If the `FalsingManager`'s belief in falsing crosses some internally defined
+threshold, it will fire an event that other parts of the system can listen for.
+This even indicates that the owner is likely actively pocket-dialing, and any
+currently open activities on the phone should retract themselves.
+
+To subscribe to this event, call
+`FalsingManager#addFalsingBeliefListener(FalsingBeliefListener listener)`.
+`FalsingBeliefListener` is a simple one method interface that will be called
+after when activities should retract themselves.
+
+**Do Listen For This**. Your code will work without it, but it is a handy,
+universal signal that will save the phone owner a lot of accidents. A simple
+implementation looks like:
+
+```
+ mFalsingManager.addFalsingBeliefListener(MyFeatureClass::hide);
+```
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
index caa9b4f..95c2d2e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
@@ -51,6 +51,15 @@
return Resources.ID_NULL;
}
+ /**
+ * @return resource id of the string to use for closing the detail panel. If
+ * {@code Resources.ID_NULL}, then use the default string:
+ * {@code com.android.systemui.R.string.quick_settings_done}
+ */
+ default int getDoneText() {
+ return Resources.ID_NULL;
+ }
+
void setToggleState(boolean state);
int getMetricsCategory();
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 6b4fbdb..6ecbe06 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -73,7 +73,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
- android:textSize="60dp"
+ android:textSize="86dp"
android:fontFamily="@font/clock"
android:typeface="monospace"
android:elegantTextHeight="false"
@@ -98,7 +98,7 @@
android:fontFamily="@font/clock"
android:typeface="monospace"
android:elegantTextHeight="false"
- dozeWeight="100"
+ dozeWeight="200"
lockScreenWeight="400"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index 8780dc5..56a71d4 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -21,17 +21,17 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="514691256816366517">"מגן מקלדת"</string>
- <string name="keyguard_password_enter_pin_code" msgid="8582296866585566671">"הזן את קוד הגישה"</string>
+ <string name="keyguard_password_enter_pin_code" msgid="8582296866585566671">"יש להזין את קוד האימות"</string>
<string name="keyguard_password_enter_puk_code" msgid="3813154965969758868">"הזן את קוד ה-PUK של כרטיס ה-SIM ולאחר מכן הזן קוד גישה חדש"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="3529260761374385243">"קוד PUK של כרטיס SIM"</string>
<string name="keyguard_password_enter_pin_prompt" msgid="2304037870481240781">"קוד גישה חדש לכרטיס ה-SIM"</string>
<string name="keyguard_password_entry_touch_hint" msgid="6180028658339706333"><font size="17">"גע כדי להזין את הסיסמה"</font></string>
<string name="keyguard_password_enter_password_code" msgid="7393393239623946777">"הזן סיסמה לביטול הנעילה"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="3692259677395250509">"הזן את קוד הגישה לביטול הנעילה"</string>
- <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"הזנת קוד גישה"</string>
+ <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"צריך להזין קוד אימות"</string>
<string name="keyguard_enter_your_pattern" msgid="351503370332324745">"יש להזין קו ביטול נעילה"</string>
<string name="keyguard_enter_your_password" msgid="7225626204122735501">"יש להזין סיסמה"</string>
- <string name="keyguard_password_wrong_pin_code" msgid="3514267777289393046">"קוד הגישה שגוי"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="3514267777289393046">"קוד האימות שגוי"</string>
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"כרטיס לא חוקי."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"הסוללה טעונה"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה אלחוטית"</string>
@@ -43,14 +43,14 @@
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"לחץ על \'תפריט\' כדי לבטל את הנעילה."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"הרשת נעולה"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"אין כרטיס SIM"</string>
- <string name="keyguard_missing_sim_instructions" msgid="1162120926141335918">"הכנס כרטיס SIM."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="1162120926141335918">"יש להכניס כרטיס SIM."</string>
<string name="keyguard_missing_sim_instructions_long" msgid="2712623293749378570">"כרטיס ה-SIM חסר או שלא ניתן לקרוא אותו. הכנס כרטיס SIM."</string>
<string name="keyguard_permanent_disabled_sim_message_short" msgid="5842745213110966962">"לא ניתן להשתמש בכרטיס SIM זה."</string>
<string name="keyguard_permanent_disabled_sim_instructions" msgid="2490584154727897806">"כרטיס ה-SIM שלך הושבת לצמיתות.\nפנה לספק השירות האלחוטי שלך לקבלת כרטיס SIM אחר."</string>
<string name="keyguard_sim_locked_message" msgid="4343544458476911044">"כרטיס ה-SIM נעול."</string>
<string name="keyguard_sim_puk_locked_message" msgid="6253830777745450550">"כרטיס ה-SIM נעול באמצעות PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="2394023844117630429">"מבטל את הנעילה של כרטיס ה-SIM…"</string>
- <string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"אזור לקוד הגישה"</string>
+ <string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"אזור של קוד האימות"</string>
<string name="keyguard_accessibility_password" msgid="3524161948484801450">"סיסמת מכשיר"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"אזור לקוד הגישה של כרטיס ה-SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="5537294043180237374">"אזור לקוד הגישה של כרטיס ה-SIM"</string>
@@ -63,7 +63,7 @@
<string name="kg_forgot_pattern_button_text" msgid="3304688032024541260">"שכחתי את קו ביטול הנעילה"</string>
<string name="kg_wrong_pattern" msgid="5907301342430102842">"קו ביטול נעילה שגוי"</string>
<string name="kg_wrong_password" msgid="4143127991071670512">"סיסמה שגויה"</string>
- <string name="kg_wrong_pin" msgid="4160978845968732624">"קוד הגישה שגוי"</string>
+ <string name="kg_wrong_pin" msgid="4160978845968732624">"קוד האימות שגוי"</string>
<plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="991400408675793914">
<item quantity="two">אפשר יהיה לנסות שוב בעוד <xliff:g id="NUMBER">%d</xliff:g> שניות.</item>
<item quantity="many">אפשר יהיה לנסות שוב בעוד <xliff:g id="NUMBER">%d</xliff:g> שניות.</item>
@@ -72,28 +72,28 @@
</plurals>
<string name="kg_pattern_instructions" msgid="5376036737065051736">"שרטט את קו ביטול הנעילה"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"הזן את קוד הגישה של כרטיס ה-SIM."</string>
- <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"הזן את קוד הגישה של כרטיס ה-SIM של <xliff:g id="CARRIER">%1$s</xliff:g>."</string>
+ <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"יש להזין את קוד האימות של כרטיס ה-SIM של <xliff:g id="CARRIER">%1$s</xliff:g>."</string>
<string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> יש להשבית את כרטיס ה-eSIM כדי להשתמש במכשיר ללא שירות סלולרי."</string>
- <string name="kg_pin_instructions" msgid="822353548385014361">"הזן קוד גישה"</string>
- <string name="kg_password_instructions" msgid="324455062831719903">"הזן את הסיסמה"</string>
+ <string name="kg_pin_instructions" msgid="822353548385014361">"יש להזין קוד אימות"</string>
+ <string name="kg_password_instructions" msgid="324455062831719903">"צריך להזין את הסיסמה"</string>
<string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"כרטיס ה-SIM מושבת כעת. הזן קוד PUK כדי להמשיך. פנה אל הספק לפרטים."</string>
- <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"ה-SIM של \"<xliff:g id="CARRIER">%1$s</xliff:g>\" מושבת כעת. הזן קוד PUK כדי להמשיך. לפרטים, פנה אל הספק."</string>
+ <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"ה-SIM של \"<xliff:g id="CARRIER">%1$s</xliff:g>\" מושבת עכשיו. צריך להזין קוד PUK כדי להמשיך. לפרטים, יש לפנות אל הספק."</string>
<string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"הזן את קוד הגישה הרצוי"</string>
- <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"אשר את קוד הגישה הרצוי"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="4251352015304070326">"מבטל את הנעילה של כרטיס ה-SIM…"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"צריך לאשר את קוד האימות הרצוי"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="4251352015304070326">"מתבצע ביטול נעילה של כרטיס ה-SIM…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"הקלד קוד גישה שאורכו 4 עד 8 ספרות."</string>
<string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"קוד PUK צריך להיות בן 8 ספרות או יותר."</string>
- <string name="kg_invalid_puk" msgid="1774337070084931186">"הזן את קוד ה-PUK הנכון. ניסיונות חוזרים ישביתו את כרטיס ה-SIM לצמיתות."</string>
+ <string name="kg_invalid_puk" msgid="1774337070084931186">"יש להזין את קוד ה-PUK הנכון. ניסיונות חוזרים ישביתו את כרטיס ה-SIM באופן סופי."</string>
<string name="kg_login_too_many_attempts" msgid="4519957179182578690">"ניסית לשרטט את קו ביטול הנעילה יותר מדי פעמים"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"הקלדת קוד גישה שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. \n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"הקלדת קוד גישה שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. \n\nיש לנסות שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"הקלדת סיסמה שגויה <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים.\n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"שרטטת קו ביטול נעילה שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. \n\nנסה שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"קוד הגישה של כרטיס ה-SIM שגוי. צור קשר עם הספק כדי לבטל את נעילת המכשיר."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="two">קוד הגישה של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות.</item>
- <item quantity="many">קוד הגישה של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות.</item>
- <item quantity="other">קוד הגישה של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות.</item>
- <item quantity="one">קוד הגישה של כרטיס ה-SIM שגוי. נותר לך עוד ניסיון <xliff:g id="NUMBER_0">%d</xliff:g> לפני שיהיה עליך ליצור קשר עם הספק כדי לבטל את נעילת המכשיר.</item>
+ <item quantity="two">קוד האימות של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות.</item>
+ <item quantity="many">קוד האימות של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות.</item>
+ <item quantity="other">קוד האימות של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות.</item>
+ <item quantity="one">קוד האימות של כרטיס ה-SIM שגוי. נותר לך עוד ניסיון <xliff:g id="NUMBER_0">%d</xliff:g> לפני שיהיה עליך ליצור קשר עם הספק כדי לבטל את נעילת המכשיר.</item>
</plurals>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"לא ניתן להשתמש בכרטיס ה-SIM. צור קשר עם הספק."</string>
<plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
@@ -103,27 +103,27 @@
<item quantity="one">קוד ה-PUK של כרטיס ה-SIM שגוי. נותר לך ניסיון <xliff:g id="NUMBER_0">%d</xliff:g> נוסף לפני שכרטיס ה-SIM יינעל לצמיתות.</item>
</plurals>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"פעולת קוד הגישה של כרטיס ה-SIM נכשלה!"</string>
- <string name="kg_password_puk_failed" msgid="6778867411556937118">"פעולת קוד ה-PUK של כרטיס ה-SIM נכשלה!"</string>
+ <string name="kg_password_puk_failed" msgid="6778867411556937118">"הניסיון לביטול הנעילה של כרטיס ה-SIM באמצעות קוד PUK נכשל!"</string>
<string name="kg_pin_accepted" msgid="1625501841604389716">"הקוד התקבל!"</string>
<string name="keyguard_carrier_default" msgid="6359808469637388586">"אין שירות."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"החלפת שיטת קלט"</string>
<string name="airplane_mode" msgid="2528005343938497866">"מצב טיסה"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"יש להזין את קו ביטול הנעילה לאחר הפעלה מחדש של המכשיר"</string>
- <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"יש להזין קוד גישה לאחר הפעלה מחדש של המכשיר"</string>
+ <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"צריך להזין קוד אימות לאחר הפעלה מחדש של המכשיר"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"יש להזין סיסמה לאחר הפעלה מחדש של המכשיר"</string>
<string name="kg_prompt_reason_timeout_pattern" msgid="9170360502528959889">"יש להזין את קו ביטול הנעילה כדי להגביר את רמת האבטחה"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="5945186097160029201">"יש להזין קוד גישה כדי להגביר את רמת האבטחה"</string>
<string name="kg_prompt_reason_timeout_password" msgid="2258263949430384278">"יש להזין סיסמה כדי להגביר את רמת האבטחה"</string>
<string name="kg_prompt_reason_switch_profiles_pattern" msgid="1922016914701991230">"יש להזין את קו ביטול הנעילה בזמן מעבר בין פרופילים"</string>
- <string name="kg_prompt_reason_switch_profiles_pin" msgid="6490434826361055400">"יש להזין את קוד הגישה בזמן מעבר בין פרופילים"</string>
+ <string name="kg_prompt_reason_switch_profiles_pin" msgid="6490434826361055400">"צריך להזין את קוד האימות כשמחליפים פרופיל"</string>
<string name="kg_prompt_reason_switch_profiles_password" msgid="1680374696393804441">"יש להזין את הסיסמה בזמן מעבר בין פרופילים"</string>
<string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"מנהל המכשיר נעל את המכשיר"</string>
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"המכשיר ננעל באופן ידני"</string>
<plurals name="kg_prompt_reason_time_pattern" formatted="false" msgid="1337428979661197957">
- <item quantity="two">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. הזן את קו ביטול הנעילה.</item>
- <item quantity="many">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. הזן את קו ביטול הנעילה.</item>
- <item quantity="other">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. הזן את קו ביטול הנעילה.</item>
- <item quantity="one">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_0">%d</xliff:g> שעה. הזן את קו ביטול הנעילה.</item>
+ <item quantity="two">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. יש להזין את קו ביטול הנעילה.</item>
+ <item quantity="many">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. יש להזין את קו ביטול הנעילה.</item>
+ <item quantity="other">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. יש להזין את קו ביטול הנעילה.</item>
+ <item quantity="one">נעילת המכשיר לא בוטלה במשך שעה אחת (<xliff:g id="NUMBER_0">%d</xliff:g>). יש להזין את קו ביטול הנעילה.</item>
</plurals>
<plurals name="kg_prompt_reason_time_pin" formatted="false" msgid="6444519502336330270">
<item quantity="two">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. הזן את קוד הגישה.</item>
@@ -132,18 +132,18 @@
<item quantity="one">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_0">%d</xliff:g> שעה. הזן את קוד הגישה.</item>
</plurals>
<plurals name="kg_prompt_reason_time_password" formatted="false" msgid="5343961527665116914">
- <item quantity="two">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. הזן את הסיסמה.</item>
- <item quantity="many">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. הזן את הסיסמה.</item>
- <item quantity="other">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. הזן את הסיסמה.</item>
- <item quantity="one">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_0">%d</xliff:g> שעה. הזן את הסיסמה.</item>
+ <item quantity="two">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. יש להזין את הסיסמה.</item>
+ <item quantity="many">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. יש להזין את הסיסמה.</item>
+ <item quantity="other">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. יש להזין את הסיסמה.</item>
+ <item quantity="one">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_0">%d</xliff:g> שעה. יש להזין את הסיסמה.</item>
</plurals>
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"לא זוהתה"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"לא זוהתה"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="two">יש להזין קוד גישה לכרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסונות נוספים.</item>
- <item quantity="many">יש להזין קוד גישה לכרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסונות נוספים.</item>
- <item quantity="other">יש להזין קוד גישה לכרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסונות נוספים.</item>
- <item quantity="one">יש להזין קוד גישה לכרטיס SIM. נותר לך <xliff:g id="NUMBER_0">%d</xliff:g> ניסיון נוסף לפני שיהיה צורך ליצור קשר עם הספק כדי לבטל את נעילת המכשיר.</item>
+ <item quantity="two">יש להזין קוד אימות של כרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
+ <item quantity="many">יש להזין קוד אימות של כרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
+ <item quantity="other">יש להזין קוד אימות של כרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
+ <item quantity="one">יש להזין קוד אימות של כרטיס SIM. נותר לך ניסיון נוסף (<xliff:g id="NUMBER_0">%d</xliff:g>) לפני שיהיה צורך ליצור קשר עם הספק כדי לבטל את נעילת המכשיר.</item>
</plurals>
<plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
<item quantity="two">כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותרו לך <xliff:g id="_NUMBER_1">%d</xliff:g> ניסיונות נוספים לפני שכרטיס ה-SIM ינעל לצמיתות. למידע נוסף, ניתן לפנות לספק שלך.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index 04419e0..47b2881 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -135,7 +135,7 @@
<item quantity="one">ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਸਿਮ ਦੇ ਪੱਕੇ ਤੌਰ \'ਤੇ ਬੇਕਾਰ ਹੋ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="_NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।</item>
<item quantity="other">ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਸਿਮ ਦੇ ਪੱਕੇ ਤੌਰ \'ਤੇ ਬੇਕਾਰ ਹੋ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="_NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।</item>
</plurals>
- <string name="clock_title_default" msgid="6342735240617459864">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
+ <string name="clock_title_default" msgid="6342735240617459864">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"ਬੁਲਬੁਲਾ"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ਐਨਾਲੌਗ"</string>
</resources>
diff --git a/packages/SystemUI/res-product/values-iw/strings.xml b/packages/SystemUI/res-product/values-iw/strings.xml
index 4ba8657..3fc8013 100644
--- a/packages/SystemUI/res-product/values-iw/strings.xml
+++ b/packages/SystemUI/res-product/values-iw/strings.xml
@@ -31,15 +31,15 @@
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"ניסית לבטל את נעילת הטאבלט <xliff:g id="NUMBER">%d</xliff:g> פעמים. הטאבלט יאופס וכל הנתונים שלו יימחקו."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"ניסית לבטל את נעילת הטלפון <xliff:g id="NUMBER">%d</xliff:g> פעמים. הטלפון יאופס וכל הנתונים שבו יימחקו."</string>
<string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"ניסית לבטל את נעילת הטאבלט <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, משתמש זה יוסר וכל נתוני המשתמש יימחקו."</string>
- <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"ניסית לבטל את נעילת הטלפון <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, משתמש זה יוסר וכל נתוני המשתמש יימחקו."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"ניסית לבטל את נעילת הטלפון <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, המשתמש הזה יוסר וכל נתוני המשתמש יימחקו."</string>
<string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"ניסית לבטל את נעילת הטאבלט <xliff:g id="NUMBER">%d</xliff:g> פעמים באופן שגוי. משתמש זה יוסר וכל נתוני המשתמש יימחקו."</string>
- <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"ניסית לבטל את נעילת הטלפון <xliff:g id="NUMBER">%d</xliff:g> פעמים. משתמש זה יוסר וכל נתוני המשתמש יימחקו."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"ניסית לבטל את נעילת הטלפון <xliff:g id="NUMBER">%d</xliff:g> פעמים. המשתמש הזה יוסר וכל נתוני המשתמש יימחקו."</string>
<string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"ניסית לבטל את נעילת הטאבלט <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, פרופיל העבודה יוסר וכל נתוני הפרופיל יימחקו."</string>
<string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"ניסית לבטל את נעילת הטלפון <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, פרופיל העבודה יוסר וכל נתוני הפרופיל יימחקו."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"ניסית לבטל את נעילת הטאבלט <xliff:g id="NUMBER">%d</xliff:g> פעמים. פרופיל העבודה יוסר וכל נתוני הפרופיל יימחקו."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"ניסית לבטל את נעילת הטלפון <xliff:g id="NUMBER">%d</xliff:g> פעמים. פרופיל העבודה יוסר וכל נתוני הפרופיל יימחקו."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"שרטטת קו ביטול נעילה שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, ,תישלח אליך בקשה לבטל את נעילת הטאבלט באמצעות חשבון אימייל.\n\n יש לנסות שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"שרטטת קו ביטול נעילה שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תשילח אליך בקשה לבטל את נעילת הטלפון באמצעות חשבון אימייל.\n\n יש לנסות שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"שרטטת קו ביטול נעילה שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תישלח אליך בקשה לבטל את נעילת הטלפון באמצעות חשבון אימייל.\n\n יש לנסות שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"לאפשרויות נוספות, יש לבטל את נעילת הטלפון"</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"לאפשרויות נוספות, יש לבטל את נעילת הטאבלט"</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"לאפשרויות נוספות, יש לבטל את נעילת המכשיר"</string>
diff --git a/packages/SystemUI/res/color/remote_input_hint.xml b/packages/SystemUI/res/color/remote_input_hint.xml
new file mode 100644
index 0000000..7fe58db
--- /dev/null
+++ b/packages/SystemUI/res/color/remote_input_hint.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="?android:attr/textColorPrimary" android:alpha=".6" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/remote_input_text.xml b/packages/SystemUI/res/color/remote_input_text.xml
index 33eeb77..3622c91 100644
--- a/packages/SystemUI/res/color/remote_input_text.xml
+++ b/packages/SystemUI/res/color/remote_input_text.xml
@@ -16,6 +16,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true" android:color="?android:attr/textColorTertiary" />
- <item android:color="?android:attr/textColorTertiary" android:alpha=".6" />
+ <item android:state_enabled="true" android:color="?android:attr/textColorPrimary" />
+ <item android:color="?android:attr/textColorPrimary" android:alpha=".6" />
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
new file mode 100644
index 0000000..f35f5d1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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="@color/notif_pill_background"
+ />
+ <corners android:radius="20dp" />
+
+ <padding
+ android:left="20dp"
+ android:right="20dp">
+ </padding>
+
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index 109442d..9f41dbe 100644
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -16,6 +16,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="#FFFFFF" />
+ <solid android:color="@color/privacy_circle" />
<corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml b/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml
index 5cb6f46..54a66e2 100644
--- a/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml
+++ b/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml
@@ -24,7 +24,7 @@
android:height="@dimen/ongoing_appops_dialog_circle_size"
android:width="@dimen/ongoing_appops_dialog_circle_size"
/>
- <solid android:color="@color/privacy_circle_camera" />
+ <solid android:color="@color/privacy_circle" />
</shape>
</item>
<item android:id="@id/icon"
diff --git a/packages/SystemUI/res/drawable/privacy_item_circle_location.xml b/packages/SystemUI/res/drawable/privacy_item_circle_location.xml
index bff9b4b..65f4396 100644
--- a/packages/SystemUI/res/drawable/privacy_item_circle_location.xml
+++ b/packages/SystemUI/res/drawable/privacy_item_circle_location.xml
@@ -24,7 +24,7 @@
android:height="@dimen/ongoing_appops_dialog_circle_size"
android:width="@dimen/ongoing_appops_dialog_circle_size"
/>
- <solid android:color="@color/privacy_circle_microphone_location" />
+ <solid android:color="@color/privacy_circle" />
</shape>
</item>
<item android:id="@id/icon"
diff --git a/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml b/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml
index 28466c8..1565d2d 100644
--- a/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml
+++ b/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml
@@ -24,7 +24,7 @@
android:height="@dimen/ongoing_appops_dialog_circle_size"
android:width="@dimen/ongoing_appops_dialog_circle_size"
/>
- <solid android:color="@color/privacy_circle_microphone_location" />
+ <solid android:color="@color/privacy_circle" />
</shape>
</item>
<item android:id="@id/icon"
diff --git a/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml b/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml
new file mode 100644
index 0000000..5d374a9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/remote_input_view_text_bg.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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="?android:attr/colorBackgroundFloating" />
+ <stroke
+ android:width="@dimen/remote_input_view_text_stroke"
+ android:color="?android:attr/colorAccent"/>
+ <padding
+ android:bottom="12dp"
+ android:left="12dp"
+ android:right="12dp"
+ android:top="12dp"/>
+
+ <corners android:radius="24dp" />
+
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 44f52ef..bbf69a9 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -18,32 +18,52 @@
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/quick_qs_status_icons"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/qs_header_top_padding"
- android:paddingBottom="@dimen/qs_header_bottom_padding"
- android:layout_below="@id/quick_status_bar_system_icons"
+ android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
- android:minHeight="20dp"
+ android:minHeight="48dp"
android:clickable="false"
android:focusable="true"
android:theme="@style/QSHeaderTheme">
- <com.android.systemui.statusbar.policy.DateView
- android:id="@+id/date"
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:gravity="center_vertical"
+ android:layout_height="match_parent"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:gravity="center_vertical|start"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:singleLine="true"
- android:textAppearance="@style/TextAppearance.QS.Status"
- systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
+
+ <View
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ />
+
+ <!-- Will hold security footer in landscape with media -->
+ <FrameLayout
+ android:id="@+id/header_text_container"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:gravity="center"
+ />
+
+ <include layout="@layout/qs_carrier_group"
+ android:id="@+id/carrier_group"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minHeight="48dp"
+ android:layout_gravity="end|center_vertical"
+ android:focusable="false"/>
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_weight="1"
android:paddingEnd="@dimen/signal_cluster_battery_padding" />
<com.android.systemui.BatteryMeterView
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
deleted file mode 100644
index fb82304..0000000
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/header_text_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_header_tooltip_height"
- android:layout_below="@id/quick_status_bar_system_icons"
- android:visibility="invisible"
- android:theme="@style/QSHeaderTheme"
- android:forceHasOverlappingRendering="false">
-
- <com.android.systemui.qs.QSHeaderInfoLayout
- android:id="@+id/status_container"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:id = "@+id/alarm_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:focusable="true"
- android:clickable="true">
-
- <ImageView
- android:id="@+id/next_alarm_icon"
- android:layout_width="@dimen/qs_header_alarm_icon_size"
- android:layout_height="@dimen/qs_header_alarm_icon_size"
- android:src="@drawable/ic_alarm"
- android:contentDescription="@string/accessibility_quick_settings_alarm_set"
- android:visibility="gone"/>
-
- <com.android.systemui.util.AutoMarqueeTextView
- android:id="@+id/next_alarm_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
- android:textAppearance="@style/TextAppearance.QS.Status"
- android:visibility="gone"/>
- </LinearLayout>
-
- <View
- android:id="@+id/status_separator"
- android:layout_width="@dimen/qs_header_separator_width"
- android:layout_height="match_parent"
- android:visibility="gone"/>
-
- <LinearLayout
- android:id = "@+id/ringer_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:focusable="true"
- android:clickable="true">
-
- <ImageView
- android:id="@+id/ringer_mode_icon"
- android:layout_width="@dimen/qs_header_alarm_icon_size"
- android:layout_height="@dimen/qs_header_alarm_icon_size"
- android:visibility="gone"/>
-
- <com.android.systemui.util.AutoMarqueeTextView
- android:id="@+id/ringer_mode_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
- android:textAppearance="@style/TextAppearance.QS.Status"
- android:visibility="gone"/>
- </LinearLayout>
- </com.android.systemui.qs.QSHeaderInfoLayout>
-
- <include layout="@layout/qs_carrier_group"
- android:id="@+id/carrier_group"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="@dimen/qs_status_separator"
- android:layout_gravity="end|center_vertical"
- android:focusable="false"/>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 059bda3..5bf6919 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -32,26 +32,32 @@
android:paddingStart="0dp"
android:elevation="4dp" >
- <!-- The clock -->
- <include layout="@layout/quick_status_bar_header_system_icons" />
+ <!-- Date and privacy. Only visible in QS -->
+ <include layout="@layout/quick_status_bar_header_date_privacy"/>
- <!-- Status icons within the panel itself (and not in the top-most status bar) -->
- <include layout="@layout/quick_qs_status_icons" />
-
- <!-- Layout containing tooltips, alarm text, etc. -->
- <include layout="@layout/quick_settings_header_info" />
+ <RelativeLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+ <!-- Time, icons and Carrier (only in QS) -->
+ <include layout="@layout/quick_qs_status_icons"/>
<com.android.systemui.qs.QuickQSPanel
android:id="@+id/quick_qs_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/quick_qs_status_icons"
- android:accessibilityTraversalAfter="@+id/date_time_group"
+ android:layout_marginTop="8dp"
+ android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
android:accessibilityTraversalBefore="@id/expand_indicator"
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
android:paddingBottom="10dp"
android:importantForAccessibility="yes" />
+ </RelativeLayout>
</com.android.systemui.qs.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
similarity index 72%
rename from packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
rename to packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index f663ab4e..22cf2cb 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -17,33 +17,35 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/quick_status_bar_system_icons"
+ android:id="@+id/quick_status_bar_date_privacy"
android:layout_width="match_parent"
android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
+ android:layout_gravity="top"
android:orientation="horizontal"
android:clickable="true"
- android:paddingTop="@dimen/status_bar_padding_top" >
+ android:paddingTop="@dimen/status_bar_padding_top"
+ android:minHeight="48dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
+ android:minHeight="48dp"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|start" >
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:minWidth="48dp"
- android:gravity="center_vertical|start"
- android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
+ <com.android.systemui.statusbar.policy.DateView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
</LinearLayout>
<android.widget.Space
@@ -56,6 +58,7 @@
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
+ android:minHeight="48dp"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|end" >
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 43182eb..ae3adb8 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -25,26 +25,25 @@
<view class="com.android.systemui.statusbar.policy.RemoteInputView$RemoteEditText"
android:id="@+id/remote_input_text"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:paddingTop="2dp"
- android:paddingBottom="4dp"
android:paddingStart="16dp"
android:paddingEnd="12dp"
- android:layout_marginRight="5dp"
+ android:layout_marginRight="20dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
- android:layout_marginBottom="20dp"
- android:gravity="start|center_vertical"
+ android:layout_marginBottom="16dp"
+ android:layout_gravity="start|center_vertical"
android:textAppearance="?android:attr/textAppearance"
android:textColor="@color/remote_input_text"
android:textColorHint="@color/remote_input_hint"
android:textSize="16sp"
android:background="@null"
- android:singleLine="true"
+ android:maxLines="4"
android:ellipsize="start"
- android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
+ android:inputType="textShortMessage|textMultiLine|textAutoCorrect|textCapSentences"
android:imeOptions="actionSend|flagNoExtractUi|flagNoFullscreen" />
<FrameLayout
@@ -53,12 +52,12 @@
android:layout_gravity="center_vertical">
<ImageButton
- android:layout_width="wrap_content"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
- android:paddingBottom="20dp"
- android:paddingStart="12dp"
- android:paddingEnd="24dp"
+ android:paddingLeft="10dp"
+ android:layout_marginBottom="12dp"
+ android:layout_marginEnd="12dp"
android:id="@+id/remote_input_send"
android:src="@drawable/ic_send"
android:contentDescription="@*android:string/ime_action_send"
@@ -70,9 +69,8 @@
android:id="@+id/remote_input_progress"
android:layout_width="24dp"
android:layout_height="24dp"
- android:layout_marginBottom="10dp"
- android:layout_marginEnd="6dp"
- android:layout_gravity="center"
+ android:layout_marginBottom="12dp"
+ android:layout_gravity="center_vertical"
android:visibility="invisible"
android:indeterminate="true"
style="?android:attr/progressBarStyleSmall" />
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 5a1e5c4..5c77d16 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -30,9 +30,11 @@
style="@style/TextAppearance.NotificationSectionHeaderButton"
android:id="@+id/manage_text"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="40dp"
android:layout_gravity="start"
+ android:background="@drawable/notif_footer_btn_background"
android:focusable="true"
+ android:textColor="@color/notif_pill_text"
android:contentDescription="@string/manage_notifications_history_text"
android:text="@string/manage_notifications_history_text"
/>
@@ -40,8 +42,9 @@
style="@style/TextAppearance.NotificationSectionHeaderButton"
android:id="@+id/dismiss_text"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="40dp"
android:layout_gravity="end"
+ android:background="@drawable/notif_footer_btn_background"
android:focusable="true"
android:contentDescription="@string/accessibility_clear_all"
android:text="@string/clear_all_notifications_text"
diff --git a/packages/SystemUI/res/layout/wireless_charging_layout.xml b/packages/SystemUI/res/layout/wireless_charging_layout.xml
index 730f24f..d82151d 100644
--- a/packages/SystemUI/res/layout/wireless_charging_layout.xml
+++ b/packages/SystemUI/res/layout/wireless_charging_layout.xml
@@ -22,6 +22,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <com.android.systemui.statusbar.charging.ChargingRippleView
+ android:id="@+id/wireless_charging_ripple"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
<!-- Circle animation -->
<ImageView
android:id="@+id/wireless_charging_view"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 06e6f29..62c37a2 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Tot sonsopkoms"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aan om <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Tot <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Verminder helderheid"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is gedeaktiveer"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is geaktiveer"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index d364370..8f67f1b 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ጸሐይ እስክትወጣ ድረስ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ላይ ይበራል"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"እስከ <xliff:g id="TIME">%s</xliff:g> ድረስ"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"ብሩህነትን ይቀንሱ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"ኤንኤፍሲ"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"ኤንኤፍሲ ተሰናክሏል"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"ኤንኤፍሲ ነቅቷል"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"ማሳያ ሁነታን አሳይ"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ኤተርኔት"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"ማንቂያ"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"ዝግጁ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"የስራ መገለጫ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"የአውሮፕላን ሁነታ"</string>
<string name="add_tile" msgid="6239678623873086686">"ሰቅ ያክሉ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 4e8ba9a..f778c70 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -420,7 +420,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"حتى شروق الشمس"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"تفعيل الوضع في <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"حتى <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"تقليل السطوع"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"تم إيقاف الاتصال القريب المدى"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"تم تفعيل الاتصال القريب المدى"</string>
@@ -664,10 +663,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"عرض الوضع التجريبي"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"إيثرنت"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"المنبّه"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"المحفظة"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"جاهزة"</string>
<string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string>
<string name="add_tile" msgid="6239678623873086686">"إضافة فئة"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index e7d838c..2d7b4ac 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"সূৰ্যোদয়লৈকে"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ত অন কৰক"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> পৰ্যন্ত"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"উজ্জ্বলতা কমাওক"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC নিষ্ক্ৰিয় হৈ আছে"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC সক্ষম হৈ আছে"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"ডেম\' ম\'ড দেখুৱাওক"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ইথাৰনেট"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"এলাৰ্ম"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"ৱালেট"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"সাজু"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"এয়াৰপ্লেইন ম\'ড"</string>
<string name="add_tile" msgid="6239678623873086686">"টাইল যোগ দিয়ক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 99b7a60..939d805 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Şəfəq vaxtına qədər"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Bu vaxt aktiv olur: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Bu vaxtadək: <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Parlaqlığı azaldın"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC deaktiv edilib"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC aktiv edilib"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Demo rejimini göstərin"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Zəngli saat"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Pulqabı"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Hazır"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Təyyarə rejimi"</string>
<string name="add_tile" msgid="6239678623873086686">"Xana əlavə edin"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index a7c14c9..213fb98 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -414,7 +414,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do izlaska sunca"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Smanjite osvetljenost"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 4d49935..8e7b9ae 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -416,7 +416,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Да ўсходу сонца"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Уключана ў <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Да <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Паменшыць яркасць"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC адключаны"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC уключаны"</string>
@@ -658,10 +657,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Паказваць дэманстрацыйны рэжым"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Будзільнік"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Кашалёк"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Гатова"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Працоўны профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Рэжым палёту"</string>
<string name="add_tile" msgid="6239678623873086686">"Дадаць плітку"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 25c757a..1ca2595 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изгрев"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ще се включи в <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Намаляване на яркостта"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"КБП е деактивирана"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"КБП е активирана"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Показване на демонстрационния режим"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Будилник"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Портфейл"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Готово"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Потребителски профил в Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Самолетен режим"</string>
<string name="add_tile" msgid="6239678623873086686">"Добавяне на плочка"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index c67d641..2bf9f29 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"সূর্যোদয় পর্যন্ত"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-এ চালু হবে"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> পর্যন্ত"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"উজ্জ্বলতা কমান"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC অক্ষম করা আছে"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC সক্ষম করা আছে"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"ডেমো মোড দেখান"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ইথারনেট"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"অ্যালার্ম"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"তৈরি"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কাজের প্রোফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"বিমান মোড"</string>
<string name="add_tile" msgid="6239678623873086686">"টাইল যোগ করুন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 6936f57..67e58dd 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -414,7 +414,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do svitanja"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Smanjenje osvjetljenja"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 8e090ec..6bdf4de 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Fins a l\'alba"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activat a les <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Fins a les <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reducció de la brillantor"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"L\'NFC està desactivada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"L\'NFC està activada"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Mostra el mode de demostració"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarma"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"A punt"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de treball"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode d\'avió"</string>
<string name="add_tile" msgid="6239678623873086686">"Afegeix un mosaic"</string>
@@ -1094,8 +1091,8 @@
<string name="timestamp" msgid="6577851592534538533">"Fa <xliff:g id="DURATION">%1$s</xliff:g>"</string>
<string name="less_than_timestamp" msgid="6598972791137724517">"Fa menys de: <xliff:g id="DURATION">%1$s</xliff:g>"</string>
<string name="over_timestamp" msgid="4765793502859358634">"Fa més de: <xliff:g id="DURATION">%1$s</xliff:g>"</string>
- <string name="birthday_status" msgid="2596961629465396761">"Natalici"</string>
- <string name="upcoming_birthday_status" msgid="2005452239256870351">"Natalici aviat"</string>
+ <string name="birthday_status" msgid="2596961629465396761">"Aniversari"</string>
+ <string name="upcoming_birthday_status" msgid="2005452239256870351">"Aniversari aviat"</string>
<string name="anniversary_status" msgid="1790034157507590838">"Aniversari"</string>
<string name="location_status" msgid="1294990572202541812">"Compartint la ubicació"</string>
<string name="new_story_status" msgid="9012195158584846525">"Història nova"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 603ccfa..67901a7 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -416,7 +416,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do svítání"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Zapnout v <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Snížit jas"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je vypnuto"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je zapnuto"</string>
@@ -658,10 +657,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Zobrazit ukázkový režim"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Budík"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Peněženka"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Připraveno"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovní profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim Letadlo"</string>
<string name="add_tile" msgid="6239678623873086686">"Přidat dlaždici"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 724c26f..489fb53 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Indtil solopgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Tænd kl. <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Indtil kl. <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reducer lysstyrken"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC er deaktiveret"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC er aktiveret"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Vis Demotilstand"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarm"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Klar"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbejdsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flytilstand"</string>
<string name="add_tile" msgid="6239678623873086686">"Tilføj et felt"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 9907dfc..61e0758 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -412,7 +412,6 @@
<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>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Bis <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Helligkeit verringern"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ist deaktiviert"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ist aktiviert"</string>
@@ -541,8 +540,7 @@
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Richtlinien ansehen"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"Jugendschutzeinstellungen"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"Dieses Gerät gehört <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nDein IT-Administrator kann Einstellungen, Zugriffsrechte im Unternehmen, Apps, mit diesem Gerät verknüpfte Daten und die Standortdaten deines Geräts sehen und verwalten.\n\nWeitere Informationen erhältst du von deinem IT-Administrator."</string>
- <!-- no translation found for monitoring_financed_description_named_management (6108439201399938668) -->
- <skip />
+ <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> kann möglicherweise auf die Daten zugreifen, die mit diesem Gerät verknüpft sind, Apps verwalten und die Geräteeinstellungen ändern.\n\nFalls du Fragen hast, wende dich an <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"Dieses Gerät gehört deiner Organisation.\n\nDein IT-Administrator kann Einstellungen, Zugriffsrechte im Unternehmen, Apps, mit diesem Gerät verknüpfte Daten und die Standortdaten deines Geräts sehen und verwalten.\n\nWeitere Informationen erhältst du von deinem IT-Administrator."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Deine Organisation hat ein Zertifikat einer Zertifizierungsstelle auf deinem Gerät installiert. Eventuell wird dein sicherer Netzwerkverkehr überwacht oder bearbeitet."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Deine Organisation hat ein Zertifikat einer Zertifizierungsstelle in deinem Arbeitsprofil installiert. Eventuell wird dein sicherer Netzwerkverkehr überwacht oder bearbeitet."</string>
@@ -653,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Demomodus anzeigen"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Weckruf"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Bereit"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbeitsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugmodus"</string>
<string name="add_tile" msgid="6239678623873086686">"Kachel hinzufügen"</string>
@@ -1021,7 +1017,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schalter"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Die Schaltfläche „Bedienungshilfen“ ersetzt die Touch-Geste für Bedienungshilfen\n\n"<annotation id="link">"Einstellungen aufrufen"</annotation></string>
- <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Schaltfläche an den Rand bewegen, um sie zeitweise auszublenden"</string>
+ <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Durch Ziehen an den Rand wird die Schaltfläche zeitweise ausgeblendet"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Gerätesteuerung"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Steuerelemente für verbundene Geräte hinzufügen"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Gerätesteuerung einrichten"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index cfae453..45d3f16 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Μέχρι την ανατολή"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ενεργοποίηση στις <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Έως <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Μείωση φωτεινότητας"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Το NFC είναι απενεργοποιημένο"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Το NFC είναι ενεργοποιημένο"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Εμφάνιση λειτουργίας επίδειξης"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Ξυπνητήρι"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Πορτοφόλι"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Έτοιμο"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Προφίλ εργασίας"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Λειτουργία πτήσης"</string>
<string name="add_tile" msgid="6239678623873086686">"Προσθήκη πλακιδίου"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index d6d97c1..126b172 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduce brightness"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index d6ae5c4..455f498 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduce brightness"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index d6d97c1..126b172 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduce brightness"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index d6d97c1..126b172 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduce brightness"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 2a2e518..8212a33 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -89,8 +89,10 @@
<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_label" msgid="8754981973544133050">"Edit"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Edit screenshot"</string>
- <string name="screenshot_scroll_label" msgid="7682877978685434621">"Scroll"</string>
- <string name="screenshot_scroll_description" msgid="7855773867093272175">"Scroll screenshot"</string>
+ <!-- no translation found for screenshot_scroll_label (7682877978685434621) -->
+ <skip />
+ <!-- no translation found for screenshot_scroll_description (7855773867093272175) -->
+ <skip />
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dismiss screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
<string name="screenshot_top_boundary" msgid="1500569103321300856">"Top boundary"</string>
@@ -412,7 +414,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduce brightness"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 70adb0b..10079a6 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hasta el amanecer"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"A la(s) <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hasta la(s) <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reducir el brillo"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"La tecnología NFC está inhabilitada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"La tecnología NFC está habilitada"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Ver en modo de demostración"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarma"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Listo"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avión"</string>
<string name="add_tile" msgid="6239678623873086686">"Agregar mosaico"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index ae28e6b..48353ae 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hasta el amanecer"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"A las <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hasta las <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reducir brillo"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"El NFC está desactivado"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"El NFC está activado"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Mostrar modo de demostración"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarma"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Listo"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
<string name="add_tile" msgid="6239678623873086686">"Añadir icono"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 09a59e7..e988c71 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Kuni päikesetõusuni"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Sisse kell <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Kuni <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Ereduse vähendamine"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC on keelatud"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC on lubatud"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Kuva demorežiim"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Äratus"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Rahakott"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Valmis"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Tööprofiil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lennukirežiim"</string>
<string name="add_tile" msgid="6239678623873086686">"Paani lisamine"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 546679f..00443dc 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Egunsentira arte"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Desaktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Murriztu distira"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Desgaituta dago NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Gaituta dago NFC"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Erakutsi demo modua"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarma"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Prest"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profila"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hegaldi modua"</string>
<string name="add_tile" msgid="6239678623873086686">"Gehitu lauza"</string>
@@ -1019,7 +1016,7 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Handitu pantaila osoa"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Handitu pantailaren zati bat"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botoia"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Erabilerraztasuna botoiak erabilerraztasun-keinuak ordezkatu ditu\n\n"<annotation id="link">"Ikusi ezarpenak"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Erabilerraztasuna botoiak erabilerraztasun-keinua ordezkatu du\n\n"<annotation id="link">"Ikusi ezarpenak"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Eraman botoia ertzera aldi baterako ezkutatzeko"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Gailuak kontrolatzeko widgetak"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Gehitu konektatutako gailuak kontrolatzeko widgetak"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 821c8e7..4f733c2 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"تا طلوع آفتاب"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ساعت <xliff:g id="TIME">%s</xliff:g> روشن میشود"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"تا<xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"کاهش روشنایی"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"ارتباط میدان نزدیک (NFC)"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"«ارتباط میدان نزدیک» (NFC) غیرفعال است"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"«ارتباط میدان نزدیک» (NFC) فعال است"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"نمایش حالت نمایشی"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"اترنت"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"زنگ"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"کیفپول"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"آماده"</string>
<string name="status_bar_work" msgid="5238641949837091056">"نمایه کاری"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"حالت هواپیما"</string>
<string name="add_tile" msgid="6239678623873086686">"افزودن کاشی"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 28e619c..47b716e 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Auringonnousuun"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Päälle klo <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> asti"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Vähennä kirkkautta"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC on poistettu käytöstä"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC on käytössä"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Näytä esittelytila"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Herätys"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Valmis"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Työprofiili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lentokonetila"</string>
<string name="add_tile" msgid="6239678623873086686">"Lisää ruutu"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 5a2a125..812bee7 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Jusqu\'à l\'aube"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Actif à <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Réduire la luminosité"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC désactivée"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC activée"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Afficher le mode Démonstration"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarme"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Prêt"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
<string name="add_tile" msgid="6239678623873086686">"Ajouter la tuile"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 9e4f1ac..8c3854e 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Jusqu\'à l\'aube"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"À partir de <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Réduire la luminosité"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC désactivée"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"La technologie NFC est activée"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Afficher le mode de démonstration"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarme"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Prêt"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
<string name="add_tile" msgid="6239678623873086686">"Ajouter un bloc"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 9091f04..334da91 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Ata o amencer"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activarase ás: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Utilizarase ata as: <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reducir brillo"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"A opción NFC está desactivada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"A opción NFC está activada"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Mostrar modo de demostración"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarma"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Listo"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de traballo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
<string name="add_tile" msgid="6239678623873086686">"Engade un atallo"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index ec286e1..c8cdff6 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"સૂર્યોદય સુધી"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> વાગ્યે ચાલુ"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> વાગ્યા સુધી"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"બ્રાઇટનેસ ઘટાડો"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC અક્ષમ કરેલ છે"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC સક્ષમ કરેલ છે"</string>
@@ -541,8 +540,7 @@
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"પૉલિસીઓ જુઓ"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"નિયંત્રણો જુઓ"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ની માલિકીનું છે.\n\nતમારા IT વ્યવસ્થાપક સેટિંગ, કૉર્પોરેટ ઍક્સેસ, ઍપ, તમારા ડિવાઇસ સાથે સંકળાયેલો ડેટા અને તમારા ડિવાઇસની સ્થાન માહિતીનું નિરીક્ષણ તેમજ તેને મેનેજ કરી શકે છે.\n\nવધુ માહિતી માટે, તમારા IT વ્યવસ્થાપકનો સંપર્ક કરો."</string>
- <!-- no translation found for monitoring_financed_description_named_management (6108439201399938668) -->
- <skip />
+ <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> આ ડિવાઇસ સાથે સંકળાયેલો ડેટા ઍક્સેસ કરી શકશે અને ઍપ મેનેજ કરી શકશે તેમજ આ ડિવાઇસના સેટિંગ બદલી શકશે.\n\nજો તમને કોઈ પ્રશ્ન હોય, તો <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>નો સંપર્ક કરો."</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"આ ડિવાઇસ તમારી સંસ્થાની માલિકીનું છે.\n\nતમારા IT વ્યવસ્થાપક સેટિંગ, કૉર્પોરેટ ઍક્સેસ, ઍપ, તમારા ડિવાઇસ સાથે સંકળાયેલો ડેટા અને તમારા ડિવાઇસની સ્થાન માહિતીનું નિરીક્ષણ તેમજ તેને મેનેજ કરી શકે છે.\n\nવધુ માહિતી માટે, તમારા IT વ્યવસ્થાપકનો સંપર્ક કરો."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"તમારી સંસ્થાએ આ ઉપકરણ પર પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કર્યું છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"તમારી સંસ્થાએ તમારી કાર્ય પ્રોફાઇલમાં પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કર્યું છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string>
@@ -653,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"ડેમો મોડ બતાવો"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ઇથરનેટ"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"એલાર્મ"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"વૉલેટ"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"તૈયાર છે"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ઑફિસની પ્રોફાઇલ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"એરપ્લેન મોડ"</string>
<string name="add_tile" msgid="6239678623873086686">"ટાઇલ ઉમેરો"</string>
@@ -1021,7 +1017,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"સ્વિચ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ઍક્સેસિબિલિટી સંકેતને ઍક્સેસિબિલિટી બટન વડે બદલવામાં આવ્યા છે\n\n"<annotation id="link">"સેટિંગ જુઓ"</annotation></string>
- <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"તેને હંગામી રૂપે ખસેડવા માટે બટનને કિનારી ખસેડો"</string>
+ <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"તેને હંગામી રૂપે ખસેડવા માટે બટનને કિનારી પર ખસેડો"</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>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 6b0c426..3126959 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -223,9 +223,7 @@
<string name="accessibility_battery_details" msgid="6184390274150865789">"बैटरी का विवरण खोलें"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> प्रतिशत बैटरी."</string>
<string name="accessibility_battery_level_with_estimate" msgid="4843119982547599452">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> प्रतिशत बैटरी बची है और आपके इस्तेमाल के हिसाब से यह <xliff:g id="TIME">%2$s</xliff:g> में खत्म हो जाएगी"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for accessibility_battery_level_charging (8892191177774027364) -->
- <skip />
+ <string name="accessibility_battery_level_charging" msgid="8892191177774027364">"बैटरी चार्ज हो रही है, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> प्रतिशत."</string>
<string name="accessibility_settings_button" msgid="2197034218538913880">"सिस्टम सेटिंग."</string>
<string name="accessibility_notifications_button" msgid="3960913924189228831">"सूचनाएं."</string>
<string name="accessibility_overflow_action" msgid="8555835828182509104">"पूरी सूचनाएं देखें"</string>
@@ -414,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सुबह तक चालू रहेगी"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> पर चालू हाेगी"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> तक चालू रहेगी"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"स्क्रीन की चमक कम करें"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"एनएफ़सी"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC बंद है"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC चालू है"</string>
@@ -654,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"डेमो मोड दिखाएं"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ईथरनेट"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"अलार्म"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"वॉलेट"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"तैयार"</string>
<string name="status_bar_work" msgid="5238641949837091056">"वर्क प्रोफ़ाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाई जहाज़ मोड"</string>
<string name="add_tile" msgid="6239678623873086686">"टाइल जोड़ें"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 2ce7fb2..0d4c079 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -414,7 +414,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do izlaska sunca"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Smanjenje svjetline"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string>
@@ -956,10 +955,10 @@
<string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
<string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je isključen"</string>
<string name="bt_is_off" msgid="7436344904889461591">"Bluetooth je isključen"</string>
- <string name="dnd_is_off" msgid="3185706903793094463">"Način Ne ometaj isključen"</string>
- <string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"Način Ne ometaj uključilo je automatsko pravilo (<xliff:g id="ID_1">%s</xliff:g>)."</string>
- <string name="qs_dnd_prompt_app" msgid="4027984447935396820">"Način Ne ometaj uključila je aplikacija (<xliff:g id="ID_1">%s</xliff:g>)."</string>
- <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Način Ne ometaj uključilo je automatsko pravilo ili aplikacija."</string>
+ <string name="dnd_is_off" msgid="3185706903793094463">"Način Ne uznemiravaj isključen"</string>
+ <string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"Način Ne uznemiravaj uključilo je automatsko pravilo (<xliff:g id="ID_1">%s</xliff:g>)."</string>
+ <string name="qs_dnd_prompt_app" msgid="4027984447935396820">"Način Ne uznemiravaj uključila je aplikacija (<xliff:g id="ID_1">%s</xliff:g>)."</string>
+ <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Način Ne uznemiravaj uključilo je automatsko pravilo ili aplikacija."</string>
<string name="qs_dnd_until" msgid="7844269319043747955">"Do <xliff:g id="ID_1">%s</xliff:g>"</string>
<string name="qs_dnd_keep" msgid="3829697305432866434">"Zadrži"</string>
<string name="qs_dnd_replace" msgid="7712119051407052689">"Zamijeni"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 66c5a82..51651ac 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Napfelkeltéig"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Be: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Eddig: <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Fényerő csökkentése"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Az NFC ki van kapcsolva"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Az NFC be van kapcsolva"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Demó mód megjelenítése"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Ébresztés"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Kész"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Munkahelyi profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Repülős üzemmód"</string>
<string name="add_tile" msgid="6239678623873086686">"Mozaik hozzáadása"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 8a79cc0..1d64b95 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Մինչև լուսաբաց"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Կմիանա՝ <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Մինչև <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Պայծառության նվազեցում"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC-ն անջատված է"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC-ն միացված է"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Ցուցադրական ռեժիմի ցուցադրում"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Զարթուցիչ"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Դրամապանակ"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Պատրաստ է"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Android for Work-ի պրոֆիլ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ավիառեժիմ"</string>
<string name="add_tile" msgid="6239678623873086686">"Սալիկի ավելացում"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 7ec11c3..21851ce 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Sampai pagi"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktif pada <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Sampai <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Kurangi kecerahan"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC dinonaktifkan"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC diaktifkan"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Tampilkan mode demo"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarm"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Siap"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode pesawat"</string>
<string name="add_tile" msgid="6239678623873086686">"Tambahkan ubin"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 304ee76..21ef9cd 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Til sólarupprásar"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Virkt kl. <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Til <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Minnka birtu"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Slökkt á NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Kveikt á NFC"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Sýna prufustillingu"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Vekjari"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Veski"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Tilbúið"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Vinnusnið"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugstilling"</string>
<string name="add_tile" msgid="6239678623873086686">"Bæta reit við"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 0a03577..5b1855b 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Fino all\'alba"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Attivazione alle <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Fino alle <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Riduci la luminosità"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC non attiva"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC attiva"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index dc32c15..8e4390e 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -28,7 +28,7 @@
<string name="battery_low_percent_format" msgid="4276661262843170964">"נותרו <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"נותרו <xliff:g id="PERCENTAGE">%1$s</xliff:g>, נשארו בערך <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>, נשארו בערך <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"נותרו <xliff:g id="PERCENTAGE">%s</xliff:g>. הופעלה תכונת החיסכון בסוללה."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"נותרו <xliff:g id="PERCENTAGE">%s</xliff:g>. התכונה \'חיסכון בסוללה\' הופעלה."</string>
<string name="invalid_charger" msgid="4370074072117767416">"לא ניתן לטעון באמצעות USB. ניתן להשתמש במטען שצורף למכשיר שלך."</string>
<string name="invalid_charger_title" msgid="938685362320735167">"לא ניתן לטעון באמצעות USB"</string>
<string name="invalid_charger_text" msgid="2339310107232691577">"שימוש במטען שסופק עם המכשיר"</string>
@@ -40,7 +40,7 @@
<string name="status_bar_settings_settings_button" msgid="534331565185171556">"הגדרות"</string>
<string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"Wi-Fi"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"סיבוב אוטומטי של המסך"</string>
- <string name="status_bar_settings_mute_label" msgid="914392730086057522">"השתק"</string>
+ <string name="status_bar_settings_mute_label" msgid="914392730086057522">"השתקה"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="2151934479226017725">"אוטומטי"</string>
<string name="status_bar_settings_notifications" msgid="5285316949980621438">"התראות"</string>
<string name="bluetooth_tethered" msgid="4171071193052799041">"Bluetooth קשור"</string>
@@ -123,8 +123,8 @@
<string name="screenrecord_start_error" msgid="2200660692479682368">"שגיאה בהפעלה של הקלטת המסך"</string>
<string name="usb_preference_title" msgid="1439924437558480718">"אפשרויות העברת קבצים ב-USB"</string>
<string name="use_mtp_button_title" msgid="5036082897886518086">"טען כנגן מדיה (MTP)"</string>
- <string name="use_ptp_button_title" msgid="7676427598943446826">"טען כמצלמה (PTP)"</string>
- <string name="installer_cd_button_title" msgid="5499998592841984743">"התקן את אפליקציית העברת הקבצים של Android עבור Mac"</string>
+ <string name="use_ptp_button_title" msgid="7676427598943446826">"טעינה כמצלמה (PTP)"</string>
+ <string name="installer_cd_button_title" msgid="5499998592841984743">"התקנת האפליקציה \'העברת קבצים ב-Android\' עבור Mac"</string>
<string name="accessibility_back" msgid="6530104400086152611">"הקודם"</string>
<string name="accessibility_home" msgid="5430449841237966217">"בית"</string>
<string name="accessibility_menu" msgid="2701163794470513040">"תפריט"</string>
@@ -142,7 +142,7 @@
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"שליחה"</string>
<string name="accessibility_manage_notification" msgid="582215815790143983">"ניהול התראות"</string>
<string name="phone_label" msgid="5715229948920451352">"פתח את הטלפון"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"פתח את המסייע הקולי"</string>
+ <string name="voice_assist_label" msgid="3725967093735929020">"פתיחת האסיסטנט"</string>
<string name="camera_label" msgid="8253821920931143699">"פתח את המצלמה"</string>
<string name="cancel" msgid="1089011503403416730">"ביטול"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"אישור"</string>
@@ -164,13 +164,13 @@
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"יש לנסות שוב. ניסיון <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> מתוך <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"הנתונים שלך יימחקו"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"הזנת קו ביטול נעילה שגוי בניסיון הבא תגרום למחיקת הנתונים במכשיר."</string>
- <string name="biometric_dialog_last_pin_attempt_before_wipe_device" msgid="9151756675698215723">"הזנת קוד גישה שגוי בניסיון הבא תגרום למחיקת הנתונים במכשיר."</string>
+ <string name="biometric_dialog_last_pin_attempt_before_wipe_device" msgid="9151756675698215723">"הזנת קוד אימות שגוי בניסיון הבא תגרום למחיקת הנתונים במכשיר."</string>
<string name="biometric_dialog_last_password_attempt_before_wipe_device" msgid="2363778585575998317">"הזנת סיסמה שגויה בניסיון הבא תגרום למחיקת הנתונים במכשיר."</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_user" msgid="8400180746043407270">"הזנת קו ביטול נעילה שגוי בניסיון הבא תגרום למחיקת המשתמש הזה."</string>
<string name="biometric_dialog_last_pin_attempt_before_wipe_user" msgid="4159878829962411168">"הזנת קוד גישה שגוי בניסיון הבא תגרום למחיקת המשתמש הזה."</string>
<string name="biometric_dialog_last_password_attempt_before_wipe_user" msgid="4695682515465063885">"הזנת סיסמה שגויה בניסיון הבא תגרום למחיקת המשתמש הזה."</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_profile" msgid="6045224069529284686">"הזנת קו ביטול נעילה שגוי בניסיון הבא תגרום למחיקת פרופיל העבודה והנתונים המשויכים אליו."</string>
- <string name="biometric_dialog_last_pin_attempt_before_wipe_profile" msgid="545567685899091757">"הזנת קוד גישה שגוי בניסיון הבא תגרום למחיקת פרופיל העבודה והנתונים המשויכים אליו."</string>
+ <string name="biometric_dialog_last_pin_attempt_before_wipe_profile" msgid="545567685899091757">"הזנה של קוד אימות שגוי בניסיון הבא תגרום למחיקת פרופיל העבודה והנתונים המשויכים אליו."</string>
<string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"הזנת סיסמה שגויה בניסיון הבא תגרום למחיקת פרופיל העבודה והנתונים המשויכים אליו."</string>
<string name="biometric_dialog_failed_attempts_now_wiping_device" msgid="6585503524026243042">"נעשו יותר מדי ניסיונות שגויים. הנתונים במכשיר יימחקו."</string>
<string name="biometric_dialog_failed_attempts_now_wiping_user" msgid="7015008539146949115">"נעשו יותר מדי ניסיונות שגויים. המשתמש הזה יימחק."</string>
@@ -208,7 +208,7 @@
<string name="accessibility_desc_on" msgid="2899626845061427845">"פועל."</string>
<string name="accessibility_desc_off" msgid="8055389500285421408">"כבוי."</string>
<string name="accessibility_desc_connected" msgid="3082590384032624233">"מחובר."</string>
- <string name="accessibility_desc_connecting" msgid="8011433412112903614">"מתחבר."</string>
+ <string name="accessibility_desc_connecting" msgid="8011433412112903614">"מתבצע חיבור."</string>
<string name="data_connection_hspa" msgid="6096234094857660873">"HSPA"</string>
<string name="data_connection_roaming" msgid="375650836665414797">"נדידה"</string>
<string name="accessibility_data_connection_wifi" msgid="4422160347472742434">"Wi-Fi"</string>
@@ -267,11 +267,11 @@
<string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth נכבה."</string>
<string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth הופעל."</string>
<string name="accessibility_quick_settings_location_off" msgid="6122523378294740598">"דיווח מיקום כבוי."</string>
- <string name="accessibility_quick_settings_location_on" msgid="6869947200325467243">"דיווח מיקום מופעל."</string>
+ <string name="accessibility_quick_settings_location_on" msgid="6869947200325467243">"התכונה \'דיווח מיקום\' מופעלת."</string>
<string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"דיווח מיקום נכבה."</string>
<string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"דיווח מיקום הופעל."</string>
<string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"ההתראה נקבעה ל-<xliff:g id="TIME">%s</xliff:g>."</string>
- <string name="accessibility_quick_settings_close" msgid="2974895537860082341">"סגור לוח."</string>
+ <string name="accessibility_quick_settings_close" msgid="2974895537860082341">"סגירת הלוח."</string>
<string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"יותר זמן."</string>
<string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"פחות זמן."</string>
<string name="accessibility_quick_settings_flashlight_off" msgid="7606563260714825190">"הפנס כבוי."</string>
@@ -301,7 +301,7 @@
<string name="data_usage_disabled_dialog" msgid="7933201635215099780">"הגעת למגבלת הנתונים שהגדרת. אתה כבר לא משתמש בחבילת גלישה.\n\nאם תמשיך, ייתכנו חיובים על שימוש בנתונים."</string>
<string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"המשך"</string>
<string name="gps_notification_searching_text" msgid="231304732649348313">"מחפש GPS"</string>
- <string name="gps_notification_found_text" msgid="3145873880174658526">"מיקום מוגדר על ידי GPS"</string>
+ <string name="gps_notification_found_text" msgid="3145873880174658526">"המיקום מוגדר על ידי GPS"</string>
<string name="accessibility_location_active" msgid="2845747916764660369">"בקשות מיקום פעילות"</string>
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"ההגדרה \'חיישנים כבויים\' פעילה"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"הסרת כל ההתראות."</string>
@@ -318,7 +318,7 @@
<string name="accessibility_rotation_lock_off" msgid="3880436123632448930">"המסך יסתובב באופן אוטומטי."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"המסך נעול כעת לרוחב."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"המסך נעול כעת לאורך."</string>
- <string name="accessibility_rotation_lock_off_changed" msgid="5772498370935088261">"המסך יסתובב כעת באופן אוטומטי."</string>
+ <string name="accessibility_rotation_lock_off_changed" msgid="5772498370935088261">"המסך יסתובב עכשיו באופן אוטומטי."</string>
<string name="accessibility_rotation_lock_on_landscape_changed" msgid="5785739044300729592">"המסך נעול כעת לרוחב."</string>
<string name="accessibility_rotation_lock_on_portrait_changed" msgid="5580170829728987989">"המסך נעול כעת לאורך."</string>
<string name="dessert_case" msgid="9104973640704357717">"מזנון קינוחים"</string>
@@ -343,12 +343,12 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"סיבוב אוטומטי"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"סיבוב אוטומטי של המסך"</string>
<string name="accessibility_quick_settings_rotation_value" msgid="2916484894750819251">"מצב <xliff:g id="ID_1">%s</xliff:g>"</string>
- <string name="quick_settings_rotation_locked_label" msgid="4420863550666310319">"סיבוב נעול"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="4420863550666310319">"סיבוב המסך נעול"</string>
<string name="quick_settings_rotation_locked_portrait_label" msgid="1194988975270484482">"לאורך"</string>
<string name="quick_settings_rotation_locked_landscape_label" msgid="2000295772687238645">"לרוחב"</string>
<string name="quick_settings_ime_label" msgid="3351174938144332051">"שיטת קלט"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"מיקום"</string>
- <string name="quick_settings_location_off_label" msgid="7923929131443915919">"מיקום כבוי"</string>
+ <string name="quick_settings_location_off_label" msgid="7923929131443915919">"המיקום מושבת"</string>
<string name="quick_settings_camera_label" msgid="1367149596242401934">"חסימת המצלמה"</string>
<string name="quick_settings_mic_label" msgid="8245831073612564953">"השתקת המיקרופון"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"מכשיר מדיה"</string>
@@ -368,7 +368,7 @@
<string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi כבוי"</string>
<string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"Wi-Fi פועל"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"אין רשתות Wi-Fi זמינות"</string>
- <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"ההפעלה מתבצעת…"</string>
+ <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"בתהליך הפעלה…"</string>
<string name="quick_settings_cast_title" msgid="2279220930629235211">"העברת מסך"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"מעביר"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"מכשיר ללא שם"</string>
@@ -400,13 +400,13 @@
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"חבילת גלישה"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"שימוש בנתונים"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="1136599216568805644">"מכסת נתונים נותרת"</string>
- <string name="quick_settings_cellular_detail_over_limit" msgid="4561921367680636235">"חריגה מההגבלה"</string>
+ <string name="quick_settings_cellular_detail_over_limit" msgid="4561921367680636235">"חריגה מהמגבלה"</string>
<string name="quick_settings_cellular_detail_data_used" msgid="6798849610647988987">"<xliff:g id="DATA_USED">%s</xliff:g> בשימוש"</string>
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"הגבלה של <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"אזהרה - <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="2754212289804324685">"פרופיל עבודה"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"תאורת לילה"</string>
- <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"מופעל בשקיעה"</string>
+ <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"התכונה מופעלת בשקיעה"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"עד הזריחה"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"מופעל בשעה <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"עד <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -416,7 +416,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"עד הזריחה"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"יתחיל בשעה <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"עד <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"הפחתה של עוצמת הבהירות"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC מושבת"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC מופעל"</string>
@@ -441,10 +440,10 @@
<string name="zen_alarms_introduction" msgid="3987266042682300470">"כדי לא להפריע לך, המכשיר לא ירטוט ולא ישמיע שום צליל, חוץ מהתראות. המצב הזה לא ישפיע על צלילים שהם חלק מתוכן שבחרת להפעיל, כמו מוזיקה, סרטונים ומשחקים."</string>
<string name="zen_priority_customize_button" msgid="4119213187257195047">"התאמה אישית"</string>
<string name="zen_silence_introduction_voice" msgid="853573681302712348">"פעולה זו מבטלת את כל הצלילים והרטט, כולל צלילים ורטט שמקורם בהתראות, מוזיקה, סרטונים ומשחקים. בכל מקרה, עדיין אפשר להתקשר."</string>
- <string name="zen_silence_introduction" msgid="6117517737057344014">"פעולה זו מבטלת את כל הצלילים והרטט, כולל בהתראות, מוזיקה, סרטונים ומשחקים."</string>
+ <string name="zen_silence_introduction" msgid="6117517737057344014">"הפעולה הזו מבטלת את כל הצלילים והרטט, כולל בהתראות, מוזיקה, סרטונים ומשחקים."</string>
<string name="keyguard_more_overflow_text" msgid="5819512373606638727">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="7248696377626341060">"התראות בדחיפות נמוכה יותר בהמשך"</string>
- <string name="notification_tap_again" msgid="4477318164947497249">"הקש שוב כדי לפתוח"</string>
+ <string name="notification_tap_again" msgid="4477318164947497249">"יש להקיש שוב כדי לפתוח את ההתראה"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"צריך להחליק כדי לפתוח"</string>
<string name="keyguard_retry" msgid="886802522584053523">"יש להחליק למעלה כדי לנסות שוב"</string>
<string name="require_unlock_for_nfc" msgid="1305686454823018831">"יש לבטל את הנעילה כדי להשתמש ב-NFC"</string>
@@ -484,7 +483,7 @@
<string name="user_logout_notification_title" msgid="3644848998053832589">"ניתוק משתמש"</string>
<string name="user_logout_notification_text" msgid="7441286737342997991">"צא מהמשתמש הנוכחי"</string>
<string name="user_logout_notification_action" msgid="7974458760719361881">"נתק משתמש"</string>
- <string name="user_add_user_title" msgid="4172327541504825032">"האם להוסיף משתמש חדש?"</string>
+ <string name="user_add_user_title" msgid="4172327541504825032">"להוסיף משתמש חדש?"</string>
<string name="user_add_user_message_short" msgid="2599370307878014791">"בעת הוספת משתמש חדש, על משתמש זה להגדיר את השטח שלו.\n\nכל משתמש יכול לעדכן אפליקציות עבור כל המשתמשים האחרים."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"הגעת למגבלת המשתמשים שניתן להוסיף"</string>
<plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
@@ -498,13 +497,13 @@
<string name="user_remove_user_remove" msgid="8387386066949061256">"הסרה"</string>
<string name="battery_saver_notification_title" msgid="8419266546034372562">"תכונת החיסכון בסוללה פועלת"</string>
<string name="battery_saver_notification_text" msgid="2617841636449016951">"מפחית את הביצועים ונתונים ברקע"</string>
- <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"כיבוי תכונת החיסכון בסוללה"</string>
+ <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"השבתת התכונה \'חיסכון בסוללה\'"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"לאפליקציית <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> תהיה גישה לכל המידע הגלוי במסך שלך ולכל תוכן שמופעל במכשיר שלך בזמן הקלטה או העברה (casting). המידע הזה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"לשירות שמספק את הפונקציה הזו תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או העברה (cast). זה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"להתחיל להקליט או להעביר (cast)?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"להתחיל להקליט או להעביר (cast) באמצעות <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
<string name="media_projection_remember_text" msgid="6896767327140422951">"אל תציג שוב"</string>
- <string name="clear_all_notifications_text" msgid="348312370303046130">"ניקוי הכל"</string>
+ <string name="clear_all_notifications_text" msgid="348312370303046130">"ניקוי הכול"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ניהול"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"היסטוריה"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"התראות חדשות"</string>
@@ -518,9 +517,9 @@
<string name="profile_owned_footer" msgid="2756770645766113964">"ייתכן שהפרופיל נתון למעקב"</string>
<string name="vpn_footer" msgid="3457155078010607471">"ייתכן שהרשת נמצאת במעקב"</string>
<string name="branded_vpn_footer" msgid="816930186313188514">"ייתכן שהרשת מנוטרת"</string>
- <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"מכשיר זה מנוהל על ידי ההורה שלך"</string>
+ <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"המכשיר הזה מנוהל על ידי ההורה שלך"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"הארגון שלך הוא הבעלים של מכשיר זה והוא עשוי לנטר את התנועה ברשת"</string>
- <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"הארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> הוא הבעלים של מכשיר זה והוא עשוי לנטר את התנועה ברשת"</string>
+ <string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"הארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> הוא הבעלים של המכשיר הזה והוא עשוי לנטר את התנועה ברשת"</string>
<string name="quick_settings_financed_disclosure_named_management" msgid="2307703784594859524">"המכשיר הזה התקבל מ-<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"המכשיר הזה שייך לארגון שלך, והוא מחובר ל-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
<string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> והוא מחובר ל-<xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
@@ -543,19 +542,18 @@
<string name="monitoring_subtitle_network_logging" msgid="2444199331891219596">"רישום התנועה ברשת"</string>
<string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"אישורי CA"</string>
<string name="disable_vpn" msgid="482685974985502922">"השבת VPN"</string>
- <string name="disconnect_vpn" msgid="26286850045344557">"נתק את ה-VPN"</string>
+ <string name="disconnect_vpn" msgid="26286850045344557">"ניתוק ה-VPN"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"הצג מדיניות"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"לצפייה באמצעי בקרת ההורים"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nמנהל ה-IT יכול לנטר ולנהל הגדרות, גישה ארגונית, אפליקציות, נתונים המשויכים למכשיר ומידע על מיקום המכשיר.\n\nלמידע נוסף, יש לפנות למנהל ה-IT."</string>
- <!-- no translation found for monitoring_financed_description_named_management (6108439201399938668) -->
- <skip />
+ <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"ל-<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> תהיה אפשרות לגשת לנתונים המשויכים למכשיר הזה, לנהל אפליקציות ולשנות את הגדרות המכשיר.\n\nאם יש לך שאלות, ניתן ליצור קשר עם <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"המכשיר הזה שייך לארגון שלך.\n\nמנהל ה-IT יכול לנטר ולנהל הגדרות, גישה ארגונית, אפליקציות, נתונים המשויכים למכשיר ומידע על מיקום המכשיר.\n\nלמידע נוסף, יש לפנות למנהל ה-IT."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"הארגון שלך התקין רשות אישורים במכשיר. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"הארגון שלך התקין רשות אישורים בפרופיל העבודה. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"במכשיר זה מותקנת רשות אישורים. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"מנהל המערכת הפעיל את התכונה \'רישום התנועה ברשת\', שמנטרת את תנועת הנתונים במכשיר."</string>
<string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"מנהל המערכת הפעיל את תכונת רישום התנועה ברשת, שמנטרת את תנועת הנתונים בפרופיל העבודה, אבל לא בפרופיל האישי."</string>
- <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"אתה מחובר לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
+ <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"התחברת לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"אתה מחובר לאפליקציות <xliff:g id="VPN_APP_0">%1$s</xliff:g> ו-<xliff:g id="VPN_APP_1">%2$s</xliff:g>, שיכולות לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"פרופיל העבודה שלך מחובר לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
<string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"הפרופיל האישי שלך מחובר לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
@@ -564,23 +562,23 @@
<string name="monitoring_description_do_body" msgid="7700878065625769970">"מנהל המערכת יכול לנטר ולנהל הגדרות, גישה ארגונית, אפליקציות, נתונים המשויכים למכשיר ומידע על מיקום המכשיר."</string>
<string name="monitoring_description_do_learn_more_separator" msgid="1467280496376492558">" "</string>
<string name="monitoring_description_do_learn_more" msgid="645149183455573790">"למידע נוסף"</string>
- <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"אתה מחובר לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
+ <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"התחברת לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
<string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
<string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"להגדרות ה-VPN"</string>
<string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string>
- <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"פתח את פרטי הכניסה המהימנים"</string>
+ <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"פתיחה של פרטי הכניסה המהימנים"</string>
<string name="monitoring_description_network_logging" msgid="577305979174002252">"מנהל המערכת הפעיל את תכונת רישום התנועה ברשת, שמנטרת את תנועת הנתונים במכשיר.\n\nלמידע נוסף, צור קשר עם מנהל המערכת."</string>
<string name="monitoring_description_vpn" msgid="1685428000684586870">"נתת לאפליקציה כלשהי הרשאה להגדיר חיבור VPN.\n\nהאפליקציה הזו יכולה לעקוב אחר הפעילות שלך ברשת ובמכשיר, כולל הודעות אימייל, אפליקציות ואתרים."</string>
<string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"פרופיל העבודה שלך מנוהל על-ידי <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\n מנהל המערכת שלך יכול לעקוב אחרי הפעילות שלך ברשת, כולל פעילות באימייל, באפליקציות ובאתרים.\n\n למידע נוסף, צור קשר עם מנהל המערכת.\n\nבנוסף, אתה מחובר ל-VPN, שגם באמצעותו ניתן לעקוב אחרי הפעילות שלך ברשת."</string>
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"מכשיר זה מנוהל על ידי ההורה שלך. להורה שלך יש אפשרות לצפות בפרטים כמו האפליקציות שבשימוש, המיקום וזמן המסך שלך, ולנהל אותם."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="monitoring_description_app" msgid="376868879287922929">"אתה מחובר לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
- <string name="monitoring_description_app_personal" msgid="1970094872688265987">"אתה מחובר לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת הפרטית, כולל הודעות אימייל, אפליקציות ואתרים."</string>
+ <string name="monitoring_description_app_personal" msgid="1970094872688265987">"התחברת לאפליקציית <xliff:g id="APPLICATION">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
<string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"אתה מחובר לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת הפרטית, כולל הודעות אימייל, אפליקציות ואתרים."</string>
<string name="monitoring_description_app_work" msgid="3713084153786663662">"פרופיל העבודה שלך מנוהל על ידי <xliff:g id="ORGANIZATION">%1$s</xliff:g>. הפרופיל מחובר לאפליקציה <xliff:g id="APPLICATION">%2$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים.\n\nלמידע נוסף, פנה למנהל המערכת."</string>
<string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"פרופיל העבודה שלך מנוהל על ידי <xliff:g id="ORGANIZATION">%1$s</xliff:g>. הפרופיל מחובר לאפליקציה <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים.\n\nהפרופיל מחובר גם לאפליקציה <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת."</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"הנעילה נמנעת על ידי סביבה אמינה"</string>
- <string name="keyguard_indication_trust_disabled" msgid="6820793704816727918">"המכשיר יישאר נעול עד שתבטל את נעילתו באופן ידני"</string>
+ <string name="keyguard_indication_trust_disabled" msgid="6820793704816727918">"המכשיר יישאר נעול עד שהנעילה שלו תבוטל באופן ידני"</string>
<string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string>
<string name="hidden_notifications_title" msgid="1782412844777612795">"קבלה מהירה של התראות"</string>
<string name="hidden_notifications_text" msgid="5899627470450792578">"צפה בהן לפני שתבטל נעילה"</string>
@@ -589,7 +587,7 @@
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="volume_zen_end_now" msgid="5901885672973736563">"כבה עכשיו"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"הגדרות צליל"</string>
- <string name="accessibility_volume_expand" msgid="7653070939304433603">"הרחב"</string>
+ <string name="accessibility_volume_expand" msgid="7653070939304433603">"הרחבה"</string>
<string name="accessibility_volume_collapse" msgid="2746845391013829996">"כווץ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"הוספת כתוביות אוטומטית למדיה"</string>
<string name="accessibility_volume_close_odi_captions_tip" msgid="8924753283621160480">"סגירת הטיפ לגבי כתוביות"</string>
@@ -613,7 +611,7 @@
<string name="screen_pinning_start" msgid="7483998671383371313">"האפליקציה הוצמדה"</string>
<string name="screen_pinning_exit" msgid="4553787518387346893">"הצמדת האפליקציה בוטלה"</string>
<string name="quick_settings_reset_confirmation_title" msgid="463533331480997595">"להסתיר<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
- <string name="quick_settings_reset_confirmation_message" msgid="2320586180785674186">"יופיע מחדש בפעם הבאה שתפעיל את האפשרות בהגדרות."</string>
+ <string name="quick_settings_reset_confirmation_message" msgid="2320586180785674186">"יופיע מחדש בפעם הבאה שהאפשרות הזו תופעל בהגדרות."</string>
<string name="quick_settings_reset_confirmation_button" msgid="3341477479055016776">"הסתר"</string>
<string name="stream_voice_call" msgid="7468348170702375660">"שיחה"</string>
<string name="stream_system" msgid="7663148785370565134">"מערכת"</string>
@@ -631,15 +629,15 @@
<string name="qs_status_phone_vibrate" msgid="7055409506885541979">"הטלפון במצב רטט"</string>
<string name="qs_status_phone_muted" msgid="3763664791309544103">"הטלפון מושתק"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. הקש כדי לבטל את ההשתקה."</string>
- <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. הקש כדי להגדיר רטט. ייתכן ששירותי הנגישות מושתקים."</string>
+ <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. צריך להקיש כדי להגדיר רטט. ייתכן ששירותי הנגישות מושתקים."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. הקש כדי להשתיק. ייתכן ששירותי הנגישות מושתקים."</string>
- <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. הקש כדי להעביר למצב רטט."</string>
- <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. הקש כדי להשתיק."</string>
+ <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. יש להקיש כדי להעביר למצב רטט."</string>
+ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. יש להקיש כדי להשתיק."</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ביטול ההשתקה"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"רטט"</string>
- <string name="volume_dialog_title" msgid="6502703403483577940">"בקרי עוצמת שמע של %s"</string>
+ <string name="volume_dialog_title" msgid="6502703403483577940">"בקרי עוצמת קול של %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"הטלפון יצלצל כשמתקבלות שיחות והתראות (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
<string name="output_title" msgid="3938776561655668350">"פלט מדיה"</string>
<string name="output_calls_title" msgid="7085583034267889109">"פלט שיחת טלפון"</string>
@@ -655,20 +653,18 @@
<string name="status_bar" msgid="4357390266055077437">"שורת סטטוס"</string>
<string name="overview" msgid="3522318590458536816">"סקירה"</string>
<string name="demo_mode" msgid="263484519766901593">"מצב הדגמה בממשק המשתמש של המערכת"</string>
- <string name="enable_demo_mode" msgid="3180345364745966431">"הפעל מצב הדגמה"</string>
- <string name="show_demo_mode" msgid="3677956462273059726">"הצג מצב הדגמה"</string>
+ <string name="enable_demo_mode" msgid="3180345364745966431">"הפעלת מצב הדגמה"</string>
+ <string name="show_demo_mode" msgid="3677956462273059726">"הצגת מצב הדגמה"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"אתרנט"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"התראה"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"ארנק"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"מוכן"</string>
<string name="status_bar_work" msgid="5238641949837091056">"פרופיל עבודה"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"מצב טיסה"</string>
- <string name="add_tile" msgid="6239678623873086686">"הוסף אריח"</string>
+ <string name="add_tile" msgid="6239678623873086686">"הוספת אריח"</string>
<string name="broadcast_tile" msgid="5224010633596487481">"אריח שידור"</string>
- <string name="zen_alarm_warning_indef" msgid="5252866591716504287">"לא תשמע את ההתראה הבאה שלך <xliff:g id="WHEN">%1$s</xliff:g>, אלא אם תשבית קודם את ההגדרה הזו"</string>
- <string name="zen_alarm_warning" msgid="7844303238486849503">"לא תשמע את ההתראה הבאה שלך <xliff:g id="WHEN">%1$s</xliff:g>"</string>
+ <string name="zen_alarm_warning_indef" msgid="5252866591716504287">"כדי לשמוע את ההתראה הבאה שלך <xliff:g id="WHEN">%1$s</xliff:g>, עליך להשבית את התכונה הזו"</string>
+ <string name="zen_alarm_warning" msgid="7844303238486849503">"לא ניתן יהיה לשמוע את ההתראה הבאה שלך <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"בשעה <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ב-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_quick_settings_detail" msgid="544463655956179791">"הגדרות מהירות, <xliff:g id="TITLE">%s</xliff:g>."</string>
@@ -682,11 +678,11 @@
<string name="remove_from_settings" msgid="633775561782209994">"הסר מההגדרות"</string>
<string name="remove_from_settings_prompt" msgid="551565437265615426">"האם להסיר את System UI Tuner ולהפסיק להשתמש בכל התכונות שלו?"</string>
<string name="activity_not_found" msgid="8711661533828200293">"האפליקציה אינה מותקנת במכשיר"</string>
- <string name="clock_seconds" msgid="8709189470828542071">"הצג שניות בשעון"</string>
+ <string name="clock_seconds" msgid="8709189470828542071">"הצגת שניות בשעון"</string>
<string name="clock_seconds_desc" msgid="2415312788902144817">"הצג שניות בשעון בשורת הסטטוס. פעולה זו עשויה להשפיע על אורך חיי הסוללה."</string>
<string name="qs_rearrange" msgid="484816665478662911">"סידור מחדש של הגדרות מהירות"</string>
<string name="show_brightness" msgid="6700267491672470007">"הצג בהירות בהגדרות מהירות"</string>
- <string name="experimental" msgid="3549865454812314826">"ניסיוני"</string>
+ <string name="experimental" msgid="3549865454812314826">"ניסיוניות"</string>
<string name="enable_bluetooth_title" msgid="866883307336662596">"האם להפעיל את ה-Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="6740938333772779717">"כדי לחבר את המקלדת לטאבלט, תחילה עליך להפעיל את ה-Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"הפעלה"</string>
@@ -793,7 +789,7 @@
<string name="battery_panel_title" msgid="5931157246673665963">"שימוש בסוללה"</string>
<string name="battery_detail_charging_summary" msgid="8821202155297559706">"תכונת החיסכון בסוללה אינה זמינה בעת טעינת המכשיר"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"חיסכון בסוללה"</string>
- <string name="battery_detail_switch_summary" msgid="3668748557848025990">"מפחית את רמת הביצועים ואת נתוני הרקע"</string>
+ <string name="battery_detail_switch_summary" msgid="3668748557848025990">"מפחיתה את רמת הביצועים ואת נתוני הרקע"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"לחצן <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"דף הבית"</string>
<string name="keyboard_key_back" msgid="4185420465469481999">"הקודם"</string>
@@ -888,7 +884,7 @@
<string-array name="clock_options">
<item msgid="3986445361435142273">"הצג שעות, דקות ושניות"</item>
<item msgid="1271006222031257266">"הצג שעות ודקות (ברירת מחדל)"</item>
- <item msgid="6135970080453877218">"אל תציג את הסמל הזה"</item>
+ <item msgid="6135970080453877218">"לא להציג את הסמל הזה"</item>
</string-array>
<string-array name="battery_options">
<item msgid="7714004721411852551">"הצג תמיד באחוזים"</item>
@@ -912,7 +908,7 @@
<string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"פתיחה של \'הגדרות מהירות\'."</string>
<string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"סגירה של \'הגדרות מהירות\'."</string>
<string name="accessibility_quick_settings_alarm_set" msgid="7237918261045099853">"הגדרת התראה."</string>
- <string name="accessibility_quick_settings_user" msgid="505821942882668619">"מחובר בתור <xliff:g id="ID_1">%s</xliff:g>"</string>
+ <string name="accessibility_quick_settings_user" msgid="505821942882668619">"בוצעה כניסה בתור <xliff:g id="ID_1">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"בחירת משתמש"</string>
<string name="data_connection_no_internet" msgid="691058178914184544">"אין אינטרנט"</string>
<string name="accessibility_quick_settings_open_details" msgid="4879279912389052142">"פתיחת פרטים."</string>
@@ -924,7 +920,7 @@
<string name="tuner_lock_screen" msgid="2267383813241144544">"מסך נעילה"</string>
<string name="thermal_shutdown_title" msgid="2702966892682930264">"הטלפון כבה עקב התחממות"</string>
<string name="thermal_shutdown_message" msgid="6142269839066172984">"הטלפון פועל כרגיל עכשיו.\nיש להקיש כדי להציג מידע נוסף"</string>
- <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"הטלפון שלך התחמם יותר מדי וכבה כדי להתקרר. הטלפון פועל כרגיל עכשיו.\n\nייתכן שהטלפון יתחמם יותר מדי אם:\n • תשתמש באפליקציות עתירות משאבים (כגון משחקים, אפליקציות וידאו או אפליקציות ניווט)\n • תוריד או תעלה קבצים גדולים\n • תשתמש בטלפון בטמפרטורות גבוהות"</string>
+ <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"הטלפון שלך התחמם יותר מדי וכבה כדי להתקרר. הטלפון פועל כרגיל עכשיו.\n\nייתכן שהטלפון יתחמם יותר מדי אם:\n • משתמשים באפליקציות עתירות משאבים (כגון משחקים, אפליקציות וידאו או אפליקציות ניווט)\n • מורידים או מעלים קבצים גדולים\n • משתמשים בטלפון בסביבה עם טמפרטורות גבוהות"</string>
<string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"לצפייה בשלבי הטיפול"</string>
<string name="high_temp_title" msgid="2218333576838496100">"הטלפון מתחמם"</string>
<string name="high_temp_notif_message" msgid="1277346543068257549">"חלק מהתכונות מוגבלות כל עוד הטלפון מתקרר.\nיש להקיש כדי להציג מידע נוסף"</string>
@@ -978,7 +974,7 @@
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"הספק שלך"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"יש אפליקציה שמסתירה את בקשת ההרשאה, ולכן להגדרות אין אפשרות לאמת את התשובה."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"האם לאפשר ל-<xliff:g id="APP_0">%1$s</xliff:g> להציג חלקים מ-<xliff:g id="APP_2">%2$s</xliff:g>?"</string>
- <string name="slice_permission_text_1" msgid="6675965177075443714">"- תהיה לה אפשרות לקרוא מידע מ-<xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="slice_permission_text_1" msgid="6675965177075443714">"- תהיה לה אפשרות לקרוא מידע מאפליקציית <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="slice_permission_text_2" msgid="6758906940360746983">"- תהיה לה יכולת לנקוט פעולה בתוך <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="slice_permission_checkbox" msgid="4242888137592298523">"יש לאשר ל-<xliff:g id="APP">%1$s</xliff:g> להראות חלקים מכל אפליציה שהיא"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"אני רוצה לאשר"</string>
@@ -1048,7 +1044,7 @@
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"סומן כהעדפה, במיקום <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="accessibility_control_not_favorite" msgid="1291760269563092359">"הוסר מהמועדפים"</string>
<string name="accessibility_control_change_favorite" msgid="2943178027582253261">"להוסיף למועדפים"</string>
- <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"להוסיף למועדפים"</string>
+ <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"להסיר מהמועדפים"</string>
<string name="accessibility_control_move" msgid="8980344493796647792">"העברה למיקום <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="controls_favorite_default_title" msgid="967742178688938137">"פקדים"</string>
<string name="controls_favorite_subtitle" msgid="6604402232298443956">"יש לבחור פקדים לגישה מתפריט ההפעלה"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 01ad3d3..44fed70 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"日の出まで"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>にオン"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>まで"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"明るさを下げる"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC は無効です"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC は有効です"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"デモモードを表示"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"イーサネット"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"アラーム"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"ウォレット"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"準備完了"</string>
<string name="status_bar_work" msgid="5238641949837091056">"仕事用プロファイル"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"機内モード"</string>
<string name="add_tile" msgid="6239678623873086686">"タイルを追加"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index cd95dd7e..cbed6a8 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"მზის ამოსვლამდე"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ჩაირთოს <xliff:g id="TIME">%s</xliff:g>-ზე"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>-მდე"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"სიკაშკაშის შემცირება"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC გათიშულია"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ჩართულია"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"დემო-რეჟიმის ჩვენება"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ეთერნეტი"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"მაღვიძარა"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"მზადაა"</string>
<string name="status_bar_work" msgid="5238641949837091056">"სამსახურის პროფილი"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"თვითმფრინავის რეჟიმი"</string>
<string name="add_tile" msgid="6239678623873086686">"მოზაიკის დამატება"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index ccdc0b6..20a326f 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн шыққанға дейін"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> дейін"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Жарықтығын азайту"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC өшірулі"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC қосулы"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Демо режимін көрсету"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Дабыл"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Әмиян"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Дайын"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жұмыс профилі"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ұшақ режимі"</string>
<string name="add_tile" msgid="6239678623873086686">"Тақтайша қосу"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 29691d1..93715e2 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"រហូតដល់ពេលថ្ងៃរះ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"បើកនៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"រហូតដល់ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"បន្ថយពន្លឺ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"បានបិទ NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"បានបើក NFC"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"បង្ហាញរបៀបសាកល្បង"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"អ៊ីសឺរណិត"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"ម៉ោងរោទ៍"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"កាបូប"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"រួចរាល់"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ប្រវត្តិរូបការងារ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"របៀបក្នុងយន្តហោះ"</string>
<string name="add_tile" msgid="6239678623873086686">"បន្ថែមក្រឡាល្អិត"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 95fb699..ba058f8 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ಸೂರ್ಯೋದಯದವರೆಗೆ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ಸಮಯದಲ್ಲಿ"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ವರೆಗೂ"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"ಪ್ರಖರತೆಯನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ಸಕ್ರಿಯಗೊಂಡಿದೆ"</string>
@@ -541,8 +540,7 @@
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"ಕಾರ್ಯನೀತಿಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"ನಿಯಂತ್ರಣಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"ಈ ಸಾಧನವು <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ಗೆ ಸೇರಿದೆ.\n\nಸೆಟ್ಟಿಂಗ್ಗಳು, ಕಾರ್ಪೊರೇಟ್ ಪ್ರವೇಶ, ಆ್ಯಪ್ಗಳು, ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಡೇಟಾ ಮತ್ತು ನಿಮ್ಮ ಸಾಧನದ ಸ್ಥಳದ ಮಾಹಿತಿಯನ್ನು ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದು.\n\nಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
- <!-- no translation found for monitoring_financed_description_named_management (6108439201399938668) -->
- <skip />
+ <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> ಗೆ ಈ ಸಾಧನದ ಜೊತೆಗೆ ಸಂಯೋಜಿತವಾಗಿರುವ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಲು, ಆ್ಯಪ್ಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಮತ್ತು ಈ ಸಾಧನಗಳ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ.\n\nನಿಮ್ಮ ಬಳಿ ಪ್ರಶ್ನೆಗಳಿದ್ದರೆ, <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g> ಅನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"ಈ ಸಾಧನವು ನಿಮ್ಮ ಸಂಸ್ಥೆಗೆ ಸೇರಿದೆ.\n\nಸೆಟ್ಟಿಂಗ್ಗಳು, ಕಾರ್ಪೊರೇಟ್ ಪ್ರವೇಶ, ಆ್ಯಪ್ಗಳು, ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಡೇಟಾ ಮತ್ತು ನಿಮ್ಮ ಸಾಧನದ ಸ್ಥಳದ ಮಾಹಿತಿಯನ್ನು ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದು.\n\nಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಸಾಧನದಲ್ಲಿ ಪ್ರಮಾಣಪತ್ರ ಅಂಗೀಕಾರವನ್ನು ಸ್ಥಾಪಿಸಿದೆ. ನಿಮ್ಮ ಸುರಕ್ಷಿತ ನೆಟ್ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಅಥವಾ ಮಾರ್ಪಡಿಸಬಹುದು."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ನಲ್ಲಿ ಪ್ರಮಾಣಪತ್ರ ಅಂಗೀಕಾರವನ್ನು ಸ್ಥಾಪಿಸಿದೆ. ನಿಮ್ಮ ಸುರಕ್ಷಿತ ನೆಟ್ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಅಥವಾ ಮಾರ್ಪಡಿಸಬಹುದು."</string>
@@ -653,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"ಡೆಮೊ ಮೋಡ್ ತೋರಿಸು"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ಇಥರ್ನೆಟ್"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"ಅಲಾರಮ್"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"ವಾಲೆಟ್"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"ಸಿದ್ಧವಾಗಿದೆ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ಏರ್ಪ್ಲೇನ್ ಮೋಡ್"</string>
<string name="add_tile" msgid="6239678623873086686">"ಟೈಲ್ ಸೇರಿಸಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 3767888..3a519e5 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"일출까지"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>에 켜짐"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>까지"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"밝기 낮추기"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 사용 중지됨"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 사용 설정됨"</string>
@@ -541,7 +540,7 @@
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"정책 보기"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"제어 기능 보기"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>에 속한 기기입니다.\n\nIT 관리자가 설정, 기업 액세스, 앱, 기기와 연결된 데이터, 기기 위치 정보를 모니터링 및 관리할 수 있습니다.\n\n자세한 정보를 확인하려면 IT 관리자에게 문의하세요."</string>
- <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g>에서 이 기기와 연결된 데이터에 액세스하고 앱을 관리하고 기기 설정을 변경할 수 있습니다.\n\n궁금한 점이 있으면 <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>에 문의하세요."</string>
+ <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g>에서 이 기기와 연결된 데이터에 액세스하고 앱을 관리하며 기기 설정을 변경할 수 있습니다.\n\n궁금한 점이 있으면 <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>에 문의하세요."</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"내 조직에 속한 기기입니다.\n\nIT 관리자가 설정, 기업 액세스, 앱, 기기와 연결된 데이터, 기기 위치 정보를 모니터링 및 관리할 수 있습니다.\n\n자세한 정보를 확인하려면 IT 관리자에게 문의하세요."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"조직에서 이 기기에 인증기관을 설치했습니다. 보안 네트워크 트래픽을 모니터링 또는 수정할 수 있습니다."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"조직에서 직장 프로필에 인증기관을 설치했습니다. 보안 네트워크 트래픽을 모니터링 또는 수정할 수 있습니다."</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"데모 모드 표시"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"이더넷"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"알람"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"준비됨"</string>
<string name="status_bar_work" msgid="5238641949837091056">"직장 프로필"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"비행기 모드"</string>
<string name="add_tile" msgid="6239678623873086686">"타일 추가"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 4897b72..f637222 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн чыкканга чейин"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Саат <xliff:g id="TIME">%s</xliff:g> күйөт"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> чейин"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Экрандын жарыктыгын төмөндөтүү"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC өчүрүлгөн"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC иштетилген"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Демо режимин көрсөтүү"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Ойготкуч"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Даяр"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жумуш профили"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Учак режими"</string>
<string name="add_tile" msgid="6239678623873086686">"Тайл кошуу"</string>
@@ -675,8 +672,8 @@
<string name="remove_from_settings" msgid="633775561782209994">"Жөндөөлөрдөн алып салуу"</string>
<string name="remove_from_settings_prompt" msgid="551565437265615426">"System UI Tuner Жөндөөлөрдөн өчүрүлүп, анын бардык функциялары токтотулсунбу?"</string>
<string name="activity_not_found" msgid="8711661533828200293">"Колдонмо сиздин түзмөгүңүздө орнотулган эмес"</string>
- <string name="clock_seconds" msgid="8709189470828542071">"Сааттын секунддары көрсөтүлсүн"</string>
- <string name="clock_seconds_desc" msgid="2415312788902144817">"Абал тилкесинен сааттын секунддары көрсөтүлсүн. Батареянын кубаты көбүрөөк сарпталышы мүмкүн."</string>
+ <string name="clock_seconds" msgid="8709189470828542071">"Сааттын секунддары көрүнсүн"</string>
+ <string name="clock_seconds_desc" msgid="2415312788902144817">"Абал тилкесинен сааттын секунддары көрүнсүн. Батареянын кубаты көбүрөөк сарпталышы мүмкүн."</string>
<string name="qs_rearrange" msgid="484816665478662911">"Ыкчам жөндөөлөрдү кайра коюу"</string>
<string name="show_brightness" msgid="6700267491672470007">"Ыкчам жөндөөлөрдөн жарык деңгээлин көрсөтүү"</string>
<string name="experimental" msgid="3549865454812314826">"Сынамык"</string>
@@ -685,7 +682,7 @@
<string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Күйгүзүү"</string>
<string name="show_silently" msgid="5629369640872236299">"Үнсүз көрүнөт"</string>
<string name="block" msgid="188483833983476566">"Бардык билдирмелерди бөгөттөө"</string>
- <string name="do_not_silence" msgid="4982217934250511227">"Үнү менен көрсөтүлсүн"</string>
+ <string name="do_not_silence" msgid="4982217934250511227">"Үнү менен көрүнсүн"</string>
<string name="do_not_silence_block" msgid="4361847809775811849">"Үнү менен көрсөтүлүп бөгөттөлбөсүн"</string>
<string name="tuner_full_importance_settings" msgid="1388025816553459059">"Эскертмелерди башкаруу каражаттары"</string>
<string name="tuner_full_importance_settings_on" msgid="917981436602311547">"Күйүк"</string>
@@ -825,7 +822,7 @@
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Музыка"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="5078136084632450333">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Жылнаама"</string>
- <string name="tuner_full_zen_title" msgid="5120366354224404511">"Үн көзөмөлдөгүчтөрү менен көрсөтүлсүн"</string>
+ <string name="tuner_full_zen_title" msgid="5120366354224404511">"Үн көзөмөлдөгүчтөрү менен көрүнсүн"</string>
<string name="volume_and_do_not_disturb" msgid="502044092739382832">"Тынчымды алба"</string>
<string name="volume_dnd_silent" msgid="4154597281458298093">"Үндү көзөмөлдөөчү баскычтардын кыска жолдору"</string>
<string name="volume_up_silent" msgid="1035180298885717790">"Үн катуулатылганда \"Тынчымды алба\" режиминен чыгуу"</string>
@@ -880,8 +877,8 @@
<item msgid="6135970080453877218">"Бул сүрөтчө көрүнбөсүн"</item>
</string-array>
<string-array name="battery_options">
- <item msgid="7714004721411852551">"Ар дайым пайызы көрсөтүлсүн"</item>
- <item msgid="3805744470661798712">"Кубаттоо учурунда пайызы көрсөтүлсүн (демейки)"</item>
+ <item msgid="7714004721411852551">"Ар дайым пайызы көрүнсүн"</item>
+ <item msgid="3805744470661798712">"Кубаттоо учурунда пайызы көрүнсүн (демейки)"</item>
<item msgid="8619482474544321778">"Бул сүрөтчө көрүнбөсүн"</item>
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 7b7c7b1..4c2a5cb 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ຈົນກວ່າຕາເວັນຂຶ້ນ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ເປີດເວລາ <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"ຈົນຮອດ <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"ຫຼຸດຄວາມສະຫວ່າງລົງ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"ສະແດງໂຫມດສາທິດ"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ອີເທເນັດ"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"ໂມງປຸກ"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"ກະເປົາ"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"ພ້ອມແລ້ວ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ໂໝດເຮືອບິນ"</string>
<string name="add_tile" msgid="6239678623873086686">"ເພີ່ມລາຍຕາກະໂລ່"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 5f17f4d..ca02dc8 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -416,7 +416,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Iki saulėtekio"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Iki <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Šviesumo mažinimas"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"ALR"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"ALR išjungtas"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"ALR įjungtas"</string>
@@ -658,10 +657,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Rodyti demonstraciniu režimu"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Eternetas"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Signalas"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Piniginė"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Paruošta"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darbo profilis"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lėktuvo režimas"</string>
<string name="add_tile" msgid="6239678623873086686">"Pridėti išklotinės elementą"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 4f17905..c355784 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -414,7 +414,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Līdz saullēktam"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Plkst. <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Līdz plkst. <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Samazināt spilgtumu"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ir atspējoti"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ir iespējoti"</string>
@@ -655,10 +654,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Rādīt demonstrācijas režīmu"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Tīkls Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Signāls"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Maks"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Gatavs"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darba profils"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lidojuma režīms"</string>
<string name="add_tile" msgid="6239678623873086686">"Pievienot elementu"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index d571df7..06718c1 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изгрејсонце"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Се вклучува во <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Намалување на осветленоста"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC е оневозможено"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC е овозможено"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Прикажи демо-режим"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Етернет"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Аларм"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Паричник"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Подготвено"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Работен профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Авионски режим"</string>
<string name="add_tile" msgid="6239678623873086686">"Додај плочка"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 131109c..36a8a54 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"സൂര്യോദയം വരെ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-ന്"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> വരെ"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"തെളിച്ചം കുറയ്ക്കുക"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC പ്രവർത്തനക്ഷമമാക്കി"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"ഡെമോ മോഡ് കാണിക്കുക"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ഇതർനെറ്റ്"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"അലാറം"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"തയ്യാറാണ്"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ഫ്ലൈറ്റ് മോഡ്"</string>
<string name="add_tile" msgid="6239678623873086686">"ടൈൽ ചേർക്കുക"</string>
@@ -716,7 +713,7 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"സ്വയമേവ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ശബ്ദമോ വൈബ്രേഷനോ ഇല്ല"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ശബ്ദമോ വൈബ്രേഷനോ ഇല്ല, സംഭാഷണ വിഭാഗത്തിന് താഴെയായി ദൃശ്യമാകും"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"ഫോൺ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ് ചെയ്തേക്കാം അല്ലെങ്കിൽ വൈബ്രേറ്റ് ചെയ്തേക്കാം"</string>
+ <string name="notification_channel_summary_default" msgid="3282930979307248890">"ഫോൺ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ്/വൈബ്രേറ്റ് ചെയ്യും"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ഫോൺ ക്രമീകരണം അടിസ്ഥാനമാക്കി റിംഗ് ചെയ്തേക്കാം അല്ലെങ്കിൽ വൈബ്രേറ്റ് ചെയ്തേക്കാം. <xliff:g id="APP_NAME">%1$s</xliff:g>-ൽ നിന്നുള്ള സംഭാഷണങ്ങൾ ഡിഫോൾട്ടായി ബബ്ൾ ആവുന്നു."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ഈ ഉള്ളടക്കത്തിലേക്ക് ഒരു ഫ്ലോട്ടിംഗ് കുറുക്കുവഴി ഉപയോഗിച്ച് നിങ്ങളുടെ ശ്രദ്ധ നിലനിർത്തുന്നു."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ഈ അറിയിപ്പ് വരുമ്പോൾ ശബ്ദിക്കുകയാണോ വൈബ്രേറ്റ് ചെയ്യുകയാണോ വേണ്ടതെന്ന് നിർണ്ണയിക്കാൻ സിസ്റ്റത്തെ അനുവദിക്കുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index a6b9055..fdf0eab 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Нар мандах хүртэл"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-д"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> хүртэл"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Гэрэлтүүлгийг багасгах"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC-г цуцалсан"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC-г идэвхжүүлсэн"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Демо горимыг харуулах"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Этернет"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Сэрүүлэг"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Түрийвч"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Бэлэн"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ажлын профайл"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Нислэгийн горим"</string>
<string name="add_tile" msgid="6239678623873086686">"Вебсайтын цонх нэмэх"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index d2baefc..8de7c96 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सूर्योदयापर्यंत"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> वाजता सुरू होते"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> पर्यंत"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"ब्राइटनेस कमी करा"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC अक्षम केले आहे"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC सक्षम केले आहे"</string>
@@ -515,8 +514,7 @@
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"हे डिव्हाइस तुमच्या पालकाने व्यवस्थापित केले आहे"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"तुमच्या संस्थेकडे या डिव्हाइसची मालकी आहे आणि ती नेटवर्क ट्रॅफिकचे परीक्षण करू शकते"</string>
<string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> च्या मालकीचे आहे आणि ती नेटवर्क ट्रॅफिकचे परीक्षण करू शकते"</string>
- <!-- no translation found for quick_settings_financed_disclosure_named_management (2307703784594859524) -->
- <skip />
+ <string name="quick_settings_financed_disclosure_named_management" msgid="2307703784594859524">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> द्वारे पुरवले गेले आहे"</string>
<string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"हे डिव्हाइस तुमच्या संस्थेचे आहे आणि ते <xliff:g id="VPN_APP">%1$s</xliff:g> ला कनेक्ट केले आहे"</string>
<string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> चे आहे आणि ते <xliff:g id="VPN_APP">%2$s</xliff:g> ला कनेक्ट केले आहे"</string>
<string name="quick_settings_disclosure_management" msgid="5515296598440684962">"हे डिव्हाइस तुमच्या संस्थेचे आहे"</string>
@@ -530,8 +528,7 @@
<string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"तुमची कार्य प्रोफाइल <xliff:g id="VPN_APP">%1$s</xliff:g> ला कनेक्ट केली"</string>
<string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"तुमची वैयक्तिक प्रोफाइल <xliff:g id="VPN_APP">%1$s</xliff:g> ला कनेक्ट केली आहे"</string>
<string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"हे डिव्हाइस <xliff:g id="VPN_APP">%1$s</xliff:g> ला कनेक्ट केले आहे"</string>
- <!-- no translation found for monitoring_title_financed_device (3659962357973919387) -->
- <skip />
+ <string name="monitoring_title_financed_device" msgid="3659962357973919387">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> द्वारे पुरवले गेले आहे"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"डिव्हाइस व्यवस्थापन"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"प्रोफाईल परीक्षण"</string>
<string name="monitoring_title" msgid="4063890083735924568">"नेटवर्क परीक्षण"</string>
@@ -654,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"डेमो मोड दर्शवा"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"इथरनेट"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"अलार्म"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"तयार आहे"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाईल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"विमान मोड"</string>
<string name="add_tile" msgid="6239678623873086686">"टाइल जोडा"</string>
@@ -904,8 +899,7 @@
<string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"जलद सेटिंग्ज बंद करा."</string>
<string name="accessibility_quick_settings_alarm_set" msgid="7237918261045099853">"अलार्म सेट केला."</string>
<string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> म्हणून साइन इन केले"</string>
- <!-- no translation found for accessibility_quick_settings_choose_user_action (4554388498186576087) -->
- <skip />
+ <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"वापरकर्ता निवडा"</string>
<string name="data_connection_no_internet" msgid="691058178914184544">"इंटरनेट नाही"</string>
<string name="accessibility_quick_settings_open_details" msgid="4879279912389052142">"तपशील उघडा."</string>
<string name="accessibility_quick_settings_not_available" msgid="6860875849497473854">"<xliff:g id="REASON">%s</xliff:g> मुळे उपलब्ध नाही"</string>
@@ -1022,10 +1016,8 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"फुल स्क्रीन मॅग्निफाय करा"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string>
- <!-- no translation found for accessibility_floating_button_migration_tooltip (4431046858918714564) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_docking_tooltip (6814897496767461517) -->
- <skip />
+ <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"अॅक्सेसिबिलिटी जेश्चर हे आता अॅक्सेसिबिलिटी बटण आहे \n\n"<annotation id="link">"सेटिंग्ज पाहा"</annotation></string>
+ <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटण तात्पुरते लपवण्यासाठी ते कोपर्यामध्ये हलवा"</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>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 4855101..50c5e0d 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hingga matahari trbt"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Dihidupkan pada <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hingga <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Kurangkan kecerahan"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC dilumpuhkan"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC didayakan"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Tunjukkan mod tunjuk cara"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Penggera"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Sedia"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod pesawat"</string>
<string name="add_tile" msgid="6239678623873086686">"Tambahkan jubin"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 91e5110..9e67f1c 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"နေထွက်ချိန် အထိ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> တွင် ဖွင့်မည်"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> အထိ"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"တောက်ပမှုကို လျှော့ခြင်း"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ကို ပိတ်ထားသည်"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ကို ဖွင့်ထားသည်"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"သရုပ်ပြမုဒ် ပြရန်"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"အီသာနက်"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"နှိုးစက်"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"အဆင်သင့်"</string>
<string name="status_bar_work" msgid="5238641949837091056">"အလုပ် ပရိုဖိုင်"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"လေယာဉ်ပျံမုဒ်"</string>
<string name="add_tile" msgid="6239678623873086686">"လေးထောင့်ကွက် ထည့်ရန်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5ebf4ae..aa7139b 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Til soloppgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Slås på klokken <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Til <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduser lysstyrken"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC er slått av"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC er slått på"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Vis demo-modus"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarm"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Klar"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work-profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flymodus"</string>
<string name="add_tile" msgid="6239678623873086686">"Legg til felt"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index b13561c..0f9aa79 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सूर्योदयसम्म"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> मा सक्रिय"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> सम्म"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"चमक घटाइयोस्"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC लाई असक्षम पारिएको छ"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC लाई सक्षम पारिएको छ"</string>
@@ -502,7 +501,7 @@
<string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थित गर्नुहोस्"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"नयाँ"</string>
- <string name="notification_section_header_gentle" msgid="6804099527336337197">"मौन"</string>
+ <string name="notification_section_header_gentle" msgid="6804099527336337197">"साइलेन्ट"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाहरू"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"वार्तालापहरू"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"सबै मौन सूचनाहरू हटाउनुहोस्"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"डेमो मोड देखाउनुहोस्"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"इथरनेट"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"अलार्म"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"वालेट"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"तयार छ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाइजहाज मोड"</string>
<string name="add_tile" msgid="6239678623873086686">"टाइल थप्नुहोस्"</string>
@@ -705,19 +702,19 @@
<string name="inline_block_button" msgid="479892866568378793">"रोक लगाउनुहोस्"</string>
<string name="inline_keep_button" msgid="299631874103662170">"देखाउने क्रम जारी राख्नुहोस्"</string>
<string name="inline_minimize_button" msgid="1474436209299333445">"सानो बनाउनुहोस्"</string>
- <string name="inline_silent_button_silent" msgid="525243786649275816">"मौन"</string>
+ <string name="inline_silent_button_silent" msgid="525243786649275816">"साइलेन्ट"</string>
<string name="inline_silent_button_stay_silent" msgid="2129254868305468743">"मौन रहनुहोस्"</string>
<string name="inline_silent_button_alert" msgid="5705343216858250354">"सतर्क गराउने"</string>
<string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"सर्तक गराइरहनुहोस्"</string>
<string name="inline_turn_off_notifications" msgid="8543989584403106071">"सूचनाहरू निष्क्रिय पार्नुहोस्"</string>
<string name="inline_keep_showing_app" msgid="4393429060390649757">"यो एपका सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string>
- <string name="notification_silence_title" msgid="8608090968400832335">"मौन"</string>
+ <string name="notification_silence_title" msgid="8608090968400832335">"साइलेन्ट"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"पूर्वनिर्धारित"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"स्वचालित"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"न घन्टी बज्छ न त कम्पन नै हुन्छ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"न घन्टी बज्छ न त कम्पन नै हुन्छ र वार्तालाप खण्डको तलतिर देखा पर्छ"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"फोनको सेटिङका आधारमा घन्टी बज्न वा कम्पन हुन सक्छ"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फोनको सेटिङका आधारमा घन्टी बज्न वा कम्पन हुन सक्छ। <xliff:g id="APP_NAME">%1$s</xliff:g> का वार्तालापहरू पूर्वनिर्धारित रूपमा बबलमा देखाइन्छन्।"</string>
+ <string name="notification_channel_summary_default" msgid="3282930979307248890">"फोनको सेटिङका आधारमा घन्टी बज्न वा भाइब्रेट हुन सक्छ"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फोनको सेटिङका आधारमा घन्टी बज्न वा भाइब्रेट हुन सक्छ। <xliff:g id="APP_NAME">%1$s</xliff:g> का वार्तालापहरू पूर्वनिर्धारित रूपमा बबलमा देखाइन्छन्।"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"फ्लोटिङ सर्टकटमार्फत यो सामग्रीतर्फ तपाईंको ध्यान आकर्षित गर्दछ।"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"सिस्टमलाई यो सूचना आउँदा ध्वनि बज्नु पर्छ वा कम्पन हुनु पर्छ भन्ने कुराको निधो गर्न दिनुहोस्"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>स्थिति:</b> सूचनालाई महत्त्वपूर्ण ठानी पूर्वनिर्धारित मोडमा सेट गरिएको छ"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index d571f2f..17fdeb3 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -33,6 +33,9 @@
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
+ <color name="notif_pill_background">@android:color/system_neutral1_800</color>
+ <color name="notif_pill_text">@android:color/system_neutral1_50</color>
+
<color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_300</color>
<color name="notification_guts_header_text_color">@color/GM2_grey_200</color>
@@ -99,8 +102,7 @@
<color name="qs_user_switcher_avatar_background">#3C4043</color>
<!-- Colors for privacy dialog. These should be changed to the new palette -->
- <color name="privacy_circle_camera">#81C995</color> <!-- g300 -->
- <color name="privacy_circle_microphone_location">#FCAD70</color> <!--o300 -->
+ <color name="privacy_circle">#81C995</color> <!-- g300 -->
<!-- Accessibility floating menu -->
<color name="accessibility_floating_menu_background">#B3000000</color> <!-- 70% -->
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index c7aabad..523b4c3 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Tot zonsopgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aan om <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Tot <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Helderheid verlagen"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is uitgeschakeld"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is ingeschakeld"</string>
@@ -606,7 +605,7 @@
<string name="screen_pinning_start" msgid="7483998671383371313">"App vastgezet"</string>
<string name="screen_pinning_exit" msgid="4553787518387346893">"App losgemaakt"</string>
<string name="quick_settings_reset_confirmation_title" msgid="463533331480997595">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> verbergen?"</string>
- <string name="quick_settings_reset_confirmation_message" msgid="2320586180785674186">"Deze wordt opnieuw weergegeven zodra u de instelling weer aanzet."</string>
+ <string name="quick_settings_reset_confirmation_message" msgid="2320586180785674186">"Deze zie je opnieuw zodra je de instelling weer aanzet."</string>
<string name="quick_settings_reset_confirmation_button" msgid="3341477479055016776">"Verbergen"</string>
<string name="stream_voice_call" msgid="7468348170702375660">"Bellen"</string>
<string name="stream_system" msgid="7663148785370565134">"Systeem"</string>
@@ -713,9 +712,9 @@
<string name="notification_alert_title" msgid="3656229781017543655">"Standaard"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen geluid of trilling"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen geluid of trilling en wordt lager in het gedeelte met gesprekken weergegeven"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen geluid of trilling en wordt lager in het gedeelte met gesprekken getoond"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan overgaan of trillen op basis van de telefooninstellingen"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan overgaan of trillen op basis van de telefooninstellingen. Gesprekken uit <xliff:g id="APP_NAME">%1$s</xliff:g> worden standaard als bubbels weergegeven."</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan overgaan of trillen op basis van de telefooninstellingen. Gesprekken uit <xliff:g id="APP_NAME">%1$s</xliff:g> worden standaard als bubbels getoond."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Trekt de aandacht met een zwevende snelkoppeling naar deze content."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Het systeem laten bepalen of deze melding geluid moet maken of moet trillen"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Status:</b> opgeschaald naar Standaard"</string>
@@ -1001,7 +1000,7 @@
<string name="priority_onboarding_behavior" msgid="5342816047020432929">"Prioriteitsgesprekken:"</string>
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Worden bovenaan het gespreksgedeelte getoond"</string>
<string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Tonen profielafbeelding op vergrendelscherm"</string>
- <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Worden als zwevende ballon weergegeven vóór apps"</string>
+ <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Zijn als zwevende ballon zichtbaar vóór apps"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"Onderbreken \'Niet storen\'"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"OK"</string>
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Instellingen"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index d4bb3e7..3bbdd30 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ସକାଳ ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ରେ ଚାଲୁ ହେବ"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"ଉଜ୍ଜ୍ୱଳତା କମ୍ କରନ୍ତୁ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ଅକ୍ଷମ କରାଯାଇଛି"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ସକ୍ଷମ କରାଯାଇଛି"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"ଡେମୋ ମୋଡ୍ ଦେଖାନ୍ତୁ"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ଇଥରନେଟ୍"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"ଆଲାର୍ମ"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"ୱାଲେଟ୍"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"ପ୍ରସ୍ତୁତ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ଏରୋପ୍ଲେନ୍ ମୋଡ୍"</string>
<string name="add_tile" msgid="6239678623873086686">"ଟାଇଲ୍ ଯୋଡ଼ନ୍ତୁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index b368c28..9a58ef6 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ਸੂਰਜ ਚੜ੍ਹਨ ਤੱਕ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ਵਜੇ ਚਾਲੂ"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ਵਜੇ ਤੱਕ"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"ਚਮਕ ਘਟਾਓ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ਨੂੰ ਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
@@ -515,8 +514,7 @@
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਤੁਹਾਡੇ ਮਾਂ-ਪਿਓ ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਕੋਲ ਇਸ ਡੀਵਾਈਸ ਦੀ ਮਲਕੀਅਤ ਹੈ ਅਤੇ ਇਹ ਨੈੱਟਵਰਕ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ"</string>
<string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ਕੋਲ ਇਸ ਡੀਵਾਈਸ ਦੀ ਮਲਕੀਅਤ ਹੈ ਅਤੇ ਇਹ ਨੈੱਟਵਰਕ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ"</string>
- <!-- no translation found for quick_settings_financed_disclosure_named_management (2307703784594859524) -->
- <skip />
+ <string name="quick_settings_financed_disclosure_named_management" msgid="2307703784594859524">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ਵੱਲੋਂ ਮੁਹੱਈਆ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
<string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ ਅਤੇ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
<string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ ਅਤੇ <xliff:g id="VPN_APP">%2$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
<string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ"</string>
@@ -530,8 +528,7 @@
<string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"ਤੁਹਾਡਾ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
<string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"ਤੁਹਾਡਾ ਨਿੱਜੀ ਪ੍ਰੋਫਾਈਲ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
<string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
- <!-- no translation found for monitoring_title_financed_device (3659962357973919387) -->
- <skip />
+ <string name="monitoring_title_financed_device" msgid="3659962357973919387">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ਵੱਲੋਂ ਮੁਹੱਈਆ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਨ"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"ਪ੍ਰੋਫਾਈਲ ਦਾ ਨਿਰੀਖਣ ਕਰਨਾ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"ਨੈੱਟਵਰਕ ਨਿਰੀਖਣ ਕਰ ਰਿਹਾ ਹੈ"</string>
@@ -543,8 +540,7 @@
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"ਨੀਤੀਆਂ ਦੇਖੋ"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"ਕੰਟਰੋਲ ਦੇਖੋ"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ।\n\nਤੁਹਾਡਾ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਸੰਬੰਧਿਤ ਸੈਟਿੰਗਾਂ, ਕਾਰਪੋਰੇਟ ਪਹੁੰਚ, ਐਪਾਂ, ਡਾਟੇ ਅਤੇ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਟਿਕਾਣਾ ਜਾਣਕਾਰੀ ਦੀ ਨਿਗਰਾਨੀ ਅਤੇ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦਾ ਹੈ।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, ਆਪਣੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
- <!-- no translation found for monitoring_financed_description_named_management (6108439201399938668) -->
- <skip />
+ <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> ਇਸ ਡੀਵਾਈਸ ਨਾਲ ਸੰਬੰਧਿਤ ਡਾਟੇ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ, ਐਪਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦੀ ਹੈ ਅਤੇ ਇਸ ਡੀਵਾਈਸ ਦੀਆਂ ਸੈਟਿੰਗਾਂ ਨੂੰ ਬਦਲ ਸਕਦੀ ਹੈ।\n\nਜੇ ਤੁਹਾਡਾ ਕੋਈ ਸਵਾਲ ਹੈ, ਤਾਂ <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g> ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ।\n\nਤੁਹਾਡਾ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਸੰਬੰਧਿਤ ਸੈਟਿੰਗਾਂ, ਕਾਰਪੋਰੇਟ ਪਹੁੰਚ, ਐਪਾਂ, ਡਾਟੇ ਅਤੇ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਟਿਕਾਣਾ ਜਾਣਕਾਰੀ ਦੀ ਨਿਗਰਾਨੀ ਅਤੇ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦਾ ਹੈ।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, ਆਪਣੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਇੱਕ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਥਾਰਟੀ ਸਥਾਪਤ ਕੀਤੀ ਗਈ ਹੈ। ਤੁਹਾਡੇ ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ ਜਾਂ ਉਸਨੂੰ ਸੋਧਿਆ ਜਾ ਸਕਦਾ ਹੈ।"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਇੱਕ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਥਾਰਟੀ ਸਥਾਪਤ ਕੀਤੀ ਗਈ ਹੈ। ਤੁਹਾਡੇ ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ ਜਾਂ ਉਸਨੂੰ ਸੋਧਿਆ ਜਾ ਸਕਦਾ ਹੈ।"</string>
@@ -655,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"ਡੈਮੋ ਮੋਡ ਦੇਖੋ"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ਈਥਰਨੈਟ"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"ਅਲਾਰਮ"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"ਵਾਲੇਟ"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"ਤਿਆਰ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
<string name="add_tile" msgid="6239678623873086686">"ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
@@ -715,7 +709,7 @@
<string name="inline_turn_off_notifications" msgid="8543989584403106071">"ਸੂਚਨਾਵਾਂ ਬੰਦ ਕਰੋ"</string>
<string name="inline_keep_showing_app" msgid="4393429060390649757">"ਕੀ ਇਸ ਐਪ ਤੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"ਸ਼ਾਂਤ"</string>
- <string name="notification_alert_title" msgid="3656229781017543655">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
+ <string name="notification_alert_title" msgid="3656229781017543655">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ਸਵੈਚਲਿਤ"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਗੱਲਬਾਤ ਸੈਕਸ਼ਨ ਵਿੱਚ ਹੇਠਲੇ ਪਾਸੇ ਦਿਸਦੀਆਂ ਹਨ"</string>
@@ -905,8 +899,7 @@
<string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਨੂੰ ਬੰਦ ਕਰੋ।"</string>
<string name="accessibility_quick_settings_alarm_set" msgid="7237918261045099853">"ਅਲਾਰਮ ਸੈੱਟ ਕੀਤਾ।"</string>
<string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> ਵਜੋਂ ਸਾਈਨ ਇਨ ਕੀਤਾ"</string>
- <!-- no translation found for accessibility_quick_settings_choose_user_action (4554388498186576087) -->
- <skip />
+ <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ਵਰਤੋਂਕਾਰ ਚੁਣੋ"</string>
<string name="data_connection_no_internet" msgid="691058178914184544">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ।"</string>
<string name="accessibility_quick_settings_open_details" msgid="4879279912389052142">"ਵੇਰਵੇ ਖੋਲ੍ਹੋ।"</string>
<string name="accessibility_quick_settings_not_available" msgid="6860875849497473854">"<xliff:g id="REASON">%s</xliff:g> ਦੇ ਕਾਰਨ ਅਣਉਪਲਬਧ ਹੈ"</string>
@@ -1023,10 +1016,8 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਵੱਡਦਰਸ਼ੀ ਕਰੋ"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ਸਵਿੱਚ"</string>
- <!-- no translation found for accessibility_floating_button_migration_tooltip (4431046858918714564) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_docking_tooltip (6814897496767461517) -->
- <skip />
+ <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨੂੰ ਪਹੁੰਚਯੋਗਤਾ ਸੰਕੇਤ ਨਾਲ ਬਦਲ ਦਿੱਤਾ ਗਿਆ\n\n"<annotation id="link">"ਸੈਟਿੰਗਾਂ ਦੇਖੋ"</annotation></string>
+ <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ਬਟਨ ਨੂੰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਲੁਕਾਉਣ ਲਈ ਕਿਨਾਰੇ \'ਤੇ ਲਿਜਾਓ"</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>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 6ac5d27..c936dc2 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -416,7 +416,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do wschodu słońca"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Włącz o <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Zmniejsz jasność"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"Komunikacja NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Komunikacja NFC jest wyłączona"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Komunikacja NFC jest włączona"</string>
@@ -658,10 +657,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Pokaż tryb demonstracyjny"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarm"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Portfel"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Gotowe"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil służbowy"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Tryb samolotowy"</string>
<string name="add_tile" msgid="6239678623873086686">"Dodaj nazwę"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 3217028..a9c454e 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até o nascer do sol"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativar: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até: <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduzir brilho"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"A NFC está desativada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"A NFC está ativada"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 8dec9dd..dde6906 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até ao amanhecer"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativado à(s) <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até à(s) <xliff:g id="TIME">%s</xliff:g>."</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduzir o brilho"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"O NFC está desativado"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"O NFC está ativado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 3217028..a9c454e 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até o nascer do sol"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativar: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até: <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduzir brilho"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"A NFC está desativada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"A NFC está ativada"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 5b82f0b..d9e48a1 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -414,7 +414,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Până la răsărit"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activată la <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Până la <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduceți luminozitatea"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Serviciul NFC este dezactivat"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Serviciul NFC este activat"</string>
@@ -655,10 +654,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Afișați modul demonstrativ"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarmă"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Gata"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil de serviciu"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod Avion"</string>
<string name="add_tile" msgid="6239678623873086686">"Adăugați o casetă"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index d59e469..2fb31e7 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -416,7 +416,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До рассвета"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Включить в <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Уменьшение яркости"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"Модуль NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Модуль NFC отключен"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Модуль NFC включен"</string>
@@ -658,10 +657,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Перейти в демонстрационный режим"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Будильник"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Кошелек"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Готово"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Рабочий профиль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим полета"</string>
<string name="add_tile" msgid="6239678623873086686">"Добавить кнопку быстрого доступа"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 9172d50..3e393bf 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"හිරු නගින තෙක්"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ට ක්රියාත්මකයි"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> තෙක්"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"දීප්තිය අඩු කරන්න"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC අබලයි"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC සබලයි"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 1dcf37e..bbca0db 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -416,7 +416,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do východu slnka"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Zapne sa o <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Zníženie jasu"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je deaktivované"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je aktivované"</string>
@@ -658,10 +657,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Zobraziť režim ukážky"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Budík"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Peňaženka"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Pripravené"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Pracovný profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim v lietadle"</string>
<string name="add_tile" msgid="6239678623873086686">"Pridať dlaždicu"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index edb94e3..def787f 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -416,7 +416,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do sončnega vzhoda"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Vklop ob <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Zmanjšanje svetlosti"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Tehnologija NFC je onemogočena"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Tehnologija NFC je omogočena"</string>
@@ -658,10 +657,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Prikaz predstavitvenega načina"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Opozorilo"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Denarnica"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Pripravljeno"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil za Android Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način za letalo"</string>
<string name="add_tile" msgid="6239678623873086686">"Dodajanje ploščice"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 2d84dfd..9e676c4 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Deri në lindje të diellit"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktiv në <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Deri në <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Redukto ndriçimin"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC është çaktivizuar"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC është aktivizuar"</string>
@@ -541,8 +540,7 @@
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Shiko politikat"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"Shiko kontrollet"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"Kjo pajisje i përket <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministratori i teknologjisë së informacionit mund të monitorojë dhe menaxhojë cilësimet, qasjen e korporatës, aplikacionet, të dhënat e lidhura me pajisjen tënde, si dhe informacionet e vendndodhjes së pajisjes tënde.\n\nPër më shumë informacione, kontakto me administratorin e teknologjisë së informacionit."</string>
- <!-- no translation found for monitoring_financed_description_named_management (6108439201399938668) -->
- <skip />
+ <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> mund të arrijë të qaset te të dhënat e lidhura me këtë pajisje, të menaxhojë aplikacionet dhe të ndryshojë cilësimet e kësaj pajisjeje.\n\nNëse ke pyetje, kontakto me <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"Kjo pajisje i përket organizatës sate.\n\nAdministratori i teknologjisë së informacionit mund të monitorojë dhe menaxhojë cilësimet, qasjen e korporatës, aplikacionet, të dhënat e lidhura me pajisjen tënde, si dhe informacionet e vendndodhjes së pajisjes tënde.\n\nPër më shumë informacione, kontakto me administratorin e teknologjisë së informacionit."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizata jote instaloi një autoritet certifikate në këtë pajisje. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizata jote instaloi një autoritet certifikate në profilin tënd të punës. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string>
@@ -653,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Shfaq modalitetin e demonstrimit"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Eternet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarmi"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Gati"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profili i punës"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modaliteti i aeroplanit"</string>
<string name="add_tile" msgid="6239678623873086686">"Shto një pllakëz"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 0ba23f8..5fcb9c79 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -414,7 +414,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изласка сунца"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Укључује се у <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Смањите осветљеност"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC је онемогућен"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC је омогућен"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 59f290c..52d459b 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Till soluppgången"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktivera kl. <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Till <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Minska ljusstyrkan"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC är inaktiverat"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC är aktiverat"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Visa demoläge"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarm"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Klar"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Jobbprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flygplansläge"</string>
<string name="add_tile" msgid="6239678623873086686">"Lägg till en ruta"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 588231e..157c576 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hadi macheo"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Itawashwa saa <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hadi saa <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Punguza ung\'aavu"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC imezimwa"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC imewashwa"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Onyesha hali ya onyesho"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethaneti"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Kengele"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Tayari"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Wasifu wa kazini"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hali ya ndegeni"</string>
<string name="add_tile" msgid="6239678623873086686">"Ongeza kigae"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 511c457..fc632fd 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"காலை வரை"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>க்கு ஆன் செய்"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> வரை"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"ஒளிர்வைக் குறைத்தல்"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC முடக்கப்பட்டது"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC இயக்கப்பட்டது"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"டெமோ முறையைக் காட்டு"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ஈதர்நெட்"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"அலாரம்"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"வாலட்"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"தயாராக உள்ளது"</string>
<string name="status_bar_work" msgid="5238641949837091056">"பணிக் கணக்கு"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"விமானப் பயன்முறை"</string>
<string name="add_tile" msgid="6239678623873086686">"டைலைச் சேர்க்கும்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 421f596..74326dc 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"సూర్యోదయం వరకు"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> కు ఆన్ అవుతుంది"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> వరకు"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"ప్రకాశాన్ని తగ్గించండి"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC నిలిపివేయబడింది"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ప్రారంభించబడింది"</string>
@@ -515,8 +514,7 @@
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ఈ పరికరాన్ని మీ తల్లి/తండ్రి మేనేజ్ చేస్తున్నారు"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ఈ పరికరం మీ సంస్థకు చెందినది, కాబట్టి అది నెట్వర్క్ ట్రాఫిక్ను పర్యవేక్షించవచ్చు"</string>
<string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"మీ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది, కాబట్టి అది నెట్వర్క్ ట్రాఫిక్ను పర్యవేక్షించవచ్చు"</string>
- <!-- no translation found for quick_settings_financed_disclosure_named_management (2307703784594859524) -->
- <skip />
+ <string name="quick_settings_financed_disclosure_named_management" msgid="2307703784594859524">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ద్వారా అందించబడింది"</string>
<string name="quick_settings_disclosure_management_named_vpn" msgid="6096715329056415588">"ఈ పరికరం మీ సంస్థకు చెందినది, ఇది <xliff:g id="VPN_APP">%1$s</xliff:g>కు కనెక్ట్ అయి ఉంది"</string>
<string name="quick_settings_disclosure_named_management_named_vpn" msgid="5302786161534380104">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది, ఇది <xliff:g id="VPN_APP">%2$s</xliff:g>కు కనెక్ట్ అయి ఉంది"</string>
<string name="quick_settings_disclosure_management" msgid="5515296598440684962">"ఈ పరికరం మీ సంస్థకు చెందినది"</string>
@@ -530,8 +528,7 @@
<string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"మీ వర్క్ ప్రొఫైల్ <xliff:g id="VPN_APP">%1$s</xliff:g>కు కనెక్ట్ చేయబడింది"</string>
<string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="5481763430080807797">"<xliff:g id="VPN_APP">%1$s</xliff:g>కు మీ వ్యక్తిగత ప్రొఫైల్ కనెక్ట్ చేయబడింది"</string>
<string name="quick_settings_disclosure_named_vpn" msgid="2350838218824492465">"ఈ పరికరం <xliff:g id="VPN_APP">%1$s</xliff:g>కు కనెక్ట్ అయి ఉంది"</string>
- <!-- no translation found for monitoring_title_financed_device (3659962357973919387) -->
- <skip />
+ <string name="monitoring_title_financed_device" msgid="3659962357973919387">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ద్వారా అందించబడింది"</string>
<string name="monitoring_title_device_owned" msgid="7029691083837606324">"పరికర నిర్వహణ"</string>
<string name="monitoring_title_profile_owned" msgid="6301118649405449568">"ప్రొఫైల్ పర్యవేక్షణ"</string>
<string name="monitoring_title" msgid="4063890083735924568">"నెట్వర్క్ పర్యవేక్షణ"</string>
@@ -543,8 +540,7 @@
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"విధానాలను వీక్షించండి"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"నియంత్రణలను చూడండి"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది.\n\nసెట్టింగ్లను, కార్పొరేట్ యాక్సెస్ను, యాప్లను, మీ పరికరానికి సంబంధించిన డేటాను, అలాగే మీ పరికరం యొక్క లొకేషన్ సమాచారాన్ని మీ IT అడ్మిన్ పర్యవేక్షించగలరు, మేనేజ్ చేయగలరు.\n\nమరింత సమాచారం కోసం, మీ IT అడ్మిన్ను సంప్రదించండి."</string>
- <!-- no translation found for monitoring_financed_description_named_management (6108439201399938668) -->
- <skip />
+ <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g>, ఈ పరికరంతో అనుబంధించబడిన డేటాను యాక్సెస్ చేయవచ్చు, యాప్లను మేనేజ్ చేయవచ్చు అలాగే ఈ పరికరాల సెట్టింగ్లను మార్చవచ్చు.\n\nమీకు ఏవైనా సందేహాలు ఉంటే, <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>ను కాంటాక్ట్ చేయండి."</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"ఈ పరికరం మీ సంస్థకు చెందినది.\n\nసెట్టింగ్లను, కార్పొరేట్ యాక్సెస్ను, యాప్లను, మీ పరికరానికి సంబంధించిన డేటాను, అలాగే మీ పరికరం యొక్క లొకేషన్ సమాచారాన్ని మీ IT అడ్మిన్ పర్యవేక్షించగలరు, మేనేజ్ చేయగలరు.\n\nమరింత సమాచారం కోసం, మీ IT అడ్మిన్ను సంప్రదించండి."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ఈ పరికరంలో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్స్టాల్ చేసింది. మీ సురక్షిత నెట్వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"మీ కార్యాలయ ప్రొఫైల్లో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్స్టాల్ చేసింది. మీ సురక్షిత నెట్వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
@@ -655,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"డెమో మోడ్ చూపు"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ఈథర్నెట్"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"అలారం"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"సిద్ధంగా ఉంది"</string>
<string name="status_bar_work" msgid="5238641949837091056">"కార్యాలయ ప్రొఫైల్"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ఎయిర్ప్లేన్ మోడ్"</string>
<string name="add_tile" msgid="6239678623873086686">"టైల్ను జోడించండి"</string>
@@ -766,7 +760,7 @@
<string name="notification_conversation_unmute" msgid="2692255619510896710">"అలర్ట్ చేయడం"</string>
<string name="notification_conversation_bubble" msgid="2242180995373949022">"బబుల్ను చూపించు"</string>
<string name="notification_conversation_unbubble" msgid="6908427185031099868">"బబుల్స్ తీసివేయి"</string>
- <string name="notification_conversation_home_screen" msgid="8347136037958438935">"హోమ్ స్క్రీన్కు జోడించు"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"హోమ్ స్క్రీన్కు జోడించండి"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"నోటిఫికేషన్ నియంత్రణలు"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"నోటిఫికేషన్ తాత్కాలిక ఆపివేత ఎంపికలు"</string>
@@ -905,8 +899,7 @@
<string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"శీఘ్ర సెట్టింగ్లను మూసివేయండి."</string>
<string name="accessibility_quick_settings_alarm_set" msgid="7237918261045099853">"అలారం సెట్ చేయబడింది."</string>
<string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> వలె సైన్ ఇన్ చేసారు"</string>
- <!-- no translation found for accessibility_quick_settings_choose_user_action (4554388498186576087) -->
- <skip />
+ <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"యూజర్ను ఎంపిక చేయండి"</string>
<string name="data_connection_no_internet" msgid="691058178914184544">"ఇంటర్నెట్ లేదు"</string>
<string name="accessibility_quick_settings_open_details" msgid="4879279912389052142">"వివరాలను తెరవండి."</string>
<string name="accessibility_quick_settings_not_available" msgid="6860875849497473854">"<xliff:g id="REASON">%s</xliff:g> కారణంగా అందుబాటులో లేదు"</string>
@@ -1023,10 +1016,8 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ఫుల్ స్క్రీన్ను మ్యాగ్నిఫై చేయండి"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"స్క్రీన్లో భాగాన్ని మాగ్నిఫై చేయండి"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"స్విచ్ చేయి"</string>
- <!-- no translation found for accessibility_floating_button_migration_tooltip (4431046858918714564) -->
- <skip />
- <!-- no translation found for accessibility_floating_button_docking_tooltip (6814897496767461517) -->
- <skip />
+ <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"యాక్సెసిబిలిటీ బటన్, యాక్సెసిబిలిటీ సంజ్ఞను భర్తీ చేసింది\n\n"<annotation id="link">"సెట్టింగ్లను చూడండి"</annotation></string>
+ <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"తాత్కాలికంగా దానిని దాచడానికి బటన్ను చివరకు తరలించండి"</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>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 4e01555..2635418 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"จนพระอาทิตย์ขึ้น"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"เปิดเวลา <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"จนถึง <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"ลดความสว่าง"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ถูกปิดใช้งาน"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"เปิดใช้งาน NFC แล้ว"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"แสดงโหมดสาธิต"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"อีเทอร์เน็ต"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"การปลุก"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"พร้อม"</string>
<string name="status_bar_work" msgid="5238641949837091056">"โปรไฟล์งาน"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"โหมดบนเครื่องบิน"</string>
<string name="add_tile" msgid="6239678623873086686">"เพิ่มไทล์"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 789c083..57e289f 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hanggang sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ma-o-on nang <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hanggang <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Bawasan ang liwanag"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Naka-disable ang NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Naka-enable ang NFC"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Ipakita ang demo mode"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarma"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Handa na"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profile sa trabaho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
<string name="add_tile" msgid="6239678623873086686">"Magdagdag ng tile"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index b236418..a36c8d24 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Sabaha kadar"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Açılacağı saat: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Şu saate kadar: <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Parlaklığı azalt"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC devre dışı"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC etkin"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Demo modunu göster"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarm"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Cüzdan"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Hazır"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Uçak modu"</string>
<string name="add_tile" msgid="6239678623873086686">"Blok ekle"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index bcff82f..f7923eb 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -416,7 +416,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До сходу сонця"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Вмикається о <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Зменшення яскравості"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC вимкнено"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ввімкнено"</string>
@@ -658,10 +657,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Показати демонстраційний режим"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Сигнал"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Гаманець"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Готово"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Робочий профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим польоту"</string>
<string name="add_tile" msgid="6239678623873086686">"Додавання опції"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 7590a50..548784d 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"طلوع آفتاب تک"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"آن ہوگی بوقت <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> تک"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"چمک کم کریں"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC غیر فعال ہے"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC فعال ہے"</string>
@@ -541,8 +540,7 @@
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"پالیسیاں دیکھیں"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"کنٹرولز دیکھیں"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"یہ آلہ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> کا ہے۔\n\nآپ کا IT منتظم ترتیبات، کارپوریٹ رسائی، ایپس، آپ کے آلہ سے وابستہ ڈیٹا اور آپ کے آلہ کے مقام کی معلومات کی نگرانی اور ان کا نظم کر سکتا ہے۔\n\nمزید معلومات کے لیے اپنے IT منتظم سے رابطہ کریں۔"</string>
- <!-- no translation found for monitoring_financed_description_named_management (6108439201399938668) -->
- <skip />
+ <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> اس آلے سے وابستہ ڈیٹا تک رسائی حاصل، ایپس کا نظم اور ان آلات کی ترتیبات کو تبدیل کر سکتا ہے۔\n\nاگر آپ سوالات پوچھنا چاہتے ہیں تو <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g> سے رابطہ کریں۔"</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"یہ آلہ آپ کی تنظیم کا ہے۔\n\nآپ کا IT منتظم ترتیبات، کارپوریٹ رسائی، ایپس، آپ کے آلہ سے وابستہ ڈیٹا اور آپ کے آلہ کے مقام کی معلومات کی نگرانی اور ان کا نظم کر سکتا ہے۔\n\nمزید معلومات کے لیے اپنے IT منتظم سے رابطہ کریں۔"</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"آپ کی تنظیم نے اس آلے پر ایک سرٹیفکیٹ کی اتھارٹی کو انسٹال کیا ہے۔ آپ کا محفوظ نیٹ ورک ٹریفک مانیٹر ہو سکتا ہے یا اس میں ترمیم کی جا سکتی ہے۔"</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"آپ کی تنظیم نے آپ کے دفتری پروفائل میں ایک سرٹیفکیٹ کی اتھارٹی کو انسٹال کیا ہے۔ آپ کا محفوظ نیٹ ورک ٹریفک مانیٹر ہو سکتا ہے یا اس میں ترمیم کی جا سکتی ہے۔"</string>
@@ -653,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"ڈیمو موڈ دکھائیں"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"ایتھرنیٹ"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"الارم"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"تیار ہے"</string>
<string name="status_bar_work" msgid="5238641949837091056">"دفتری پروفائل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ہوائی جہاز وضع"</string>
<string name="add_tile" msgid="6239678623873086686">"ٹائل شامل کریں"</string>
@@ -1017,8 +1013,7 @@
<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>
- <!-- no translation found for magnification_mode_switch_state_full_screen (5229653514979530561) -->
- <skip />
+ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"فُل اسکرین کو بڑا کریں"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"سوئچ کریں"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ایکسیسبیلٹی بٹن کو ایکسیسبیلٹی اشارے سے بدل دیا گیا\n\n"<annotation id="link">"ترتیبات دیکھیں"</annotation></string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index eace130..42da6f5 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Quyosh chiqqunicha"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> da yoqiladi"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> gacha"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Yorqinlikni pasaytirish"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC o‘chiq"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC yoniq"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Demo rejimni ko‘rsatish"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Signal"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Tayyor"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ish profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Parvoz rejimi"</string>
<string name="add_tile" msgid="6239678623873086686">"Tezkor sozlamalar tugmasini qo‘shish"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 4640aa4..a137a12 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Cho đến khi trời sáng"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Bật vào lúc <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Cho đến <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Giảm độ sáng"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC đã được tắt"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC đã được bật"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Hiển thị chế độ trình diễn"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Báo thức"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"Ví"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Sẵn sàng"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Hồ sơ công việc"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Chế độ máy bay"</string>
<string name="add_tile" msgid="6239678623873086686">"Thêm ô"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 236879a..b78569c 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"在日出时关闭"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"在<xliff:g id="TIME">%s</xliff:g> 开启"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"直到<xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"调低亮度"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已启用"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"显示演示模式"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"以太网"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"闹钟"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"电子钱包"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"已可使用"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作资料"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飞行模式"</string>
<string name="add_tile" msgid="6239678623873086686">"添加图块"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index c204ffd..f5481df 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"在日出時關閉"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"於<xliff:g id="TIME">%s</xliff:g>開啟"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"直至<xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"調低亮度"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已啟用"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"顯示示範模式"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"以太網"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"鬧鐘"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"電子錢包"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"已可使用"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作設定檔"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛行模式"</string>
<string name="add_tile" msgid="6239678623873086686">"加入圖塊"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index fedd3c06..b7cbed1 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"於日出時關閉"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"開啟時間:<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"關閉時間:<xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"調低亮度"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已啟用"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"顯示示範模式"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"乙太網路"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"鬧鐘"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"電子錢包"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"已可使用"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作資料夾"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛航模式"</string>
<string name="add_tile" msgid="6239678623873086686">"新增圖塊"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index aa95cfa..c095383 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -412,7 +412,6 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Kuze kube sekuphumeni kwelanga"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Kuvulwe ngo-<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Kuze kube ngu-<xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Nciphisa ukukhanya"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"I-NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"I-NFC ikhutshaziwe"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"I-NFC inikwe amandla"</string>
@@ -652,10 +651,8 @@
<string name="show_demo_mode" msgid="3677956462273059726">"Bonisa imodi yedemo"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"I-Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"I-alamu"</string>
- <!-- no translation found for wallet_title (5369767670735827105) -->
- <skip />
- <!-- no translation found for wallet_secondary_label (2017028770884957543) -->
- <skip />
+ <string name="wallet_title" msgid="5369767670735827105">"I-wallet"</string>
+ <string name="wallet_secondary_label" msgid="2017028770884957543">"Isikulungele"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Iphrofayela yomsebenzi"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Imodi yendiza"</string>
<string name="add_tile" msgid="6239678623873086686">"Engeza ithayili"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a773b22..7a6de75 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -151,8 +151,8 @@
<color name="minimize_dock_shadow_end">#00000000</color>
<color name="default_remote_input_background">@*android:color/notification_default_color</color>
- <color name="remote_input_hint">#99ffffff</color>
-
+ <color name="notif_pill_background">@android:color/system_neutral2_100</color>
+ <color name="notif_pill_text">@android:color/system_neutral1_900</color>
<color name="remote_input_accent">?android:attr/colorAccent</color>
<color name="quick_step_track_background_background_dark">#1F000000</color>
@@ -272,8 +272,7 @@
<color name="screenrecord_status_color">#E94235</color>
<!-- TODO(b/178093014) Colors for privacy dialog. These should be changed to the new palette -->
- <color name="privacy_circle_camera">#1E8E3E</color> <!-- g600 -->
- <color name="privacy_circle_microphone_location">#E8710A</color> <!--o600 -->
+ <color name="privacy_circle">#1E8E3E</color> <!-- g600 -->
<!-- Accessibility floating menu -->
<color name="accessibility_floating_menu_background">#CCFFFFFF</color> <!-- 80% -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index fbe7175..6e61148 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -99,7 +99,7 @@
<!-- The default tiles to display in QuickSettings -->
<string name="quick_settings_tiles_default" translatable="false">
- wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast,screenrecord
+ internet,wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast,screenrecord
</string>
<!-- The minimum number of tiles to display in QuickSettings -->
@@ -107,7 +107,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls,alarm,wallet
+ internet,wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls,alarm,wallet
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d5c6398..be00594 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -19,6 +19,8 @@
<!-- Recommended minimum clickable element dimension -->
<dimen name="min_clickable_item_size">48dp</dimen>
+ <dimen name="remote_input_view_text_stroke">3dp</dimen>
+
<!-- Amount to offset bottom of notification peek window from top of status bar. -->
<dimen name="peek_window_y_offset">-12dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 52a97b1..2f3fbe7 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -45,8 +45,6 @@
<bool name="flag_toast_style">false</bool>
- <bool name="flag_navigation_bar_overlay">false</bool>
-
<bool name="flag_pm_lite">false</bool>
<bool name="flag_alarm_tile">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5e70f18..a815318 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -424,6 +424,12 @@
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
<!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_fingerprint_dialog_fingerprint_icon">Fingerprint icon</string>
+ <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
+ <string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string>
+ <!-- Message shown to ask the user to use fingerprint to continue.[CHAR LIMIT=50] -->
+ <string name="fingerprint_dialog_use_fingerprint">Use your fingerprint to continue</string>
+ <!-- Message shown to ask the user to use screenlock to continue.[CHAR LIMIT=NONE] -->
+ <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock">Can\u2019t recognize fingerprint. Use screen lock instead.</string>
<!-- Message shown when the system-provided face dialog is shown, asking for authentication [CHAR LIMIT=30] -->
<string name="face_dialog_looking_for_face">Looking for you\u2026</string>
@@ -891,6 +897,8 @@
<string name="quick_settings_more_user_settings">User settings</string>
<!-- QuickSettings: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
<string name="quick_settings_done">Done</string>
+ <!-- QuickSettings: Control panel: Label for button that dismisses user switcher control panel. [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_close_user_panel">Close</string>
<!-- QuickSettings: Control panel: Label for connected device. [CHAR LIMIT=NONE] -->
<string name="quick_settings_connected">Connected</string>
<!-- QuickSettings: Control panel: Label for connected device, showing remote device battery level. [CHAR LIMIT=NONE] -->
@@ -1282,6 +1290,9 @@
<!-- Disclosure at the bottom of Quick Settings that indicates that the device has a managed profile which can be monitored by the profile owner [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_named_managed_profile_monitoring"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may monitor network traffic in your work profile</string>
+ <!-- Disclosure at the bottom of Quick Settings that indicates that the user's device has a managed profile where network activity is visible to their IT admin [CHAR LIMIT=100] -->
+ <string name="quick_settings_disclosure_managed_profile_network_activity">Work profile network activity is visible to your IT admin</string>
+
<!-- Disclosure at the bottom of Quick Settings that indicates that a certificate authorithy is installed on this device and the traffic might be monitored [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_monitoring">Network may be monitored</string>
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
index d2bff18..b2bf6da 100644
--- a/packages/SystemUI/res/xml/people_space_widget_info.xml
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -16,9 +16,9 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="120dp"
- android:minHeight="54dp"
+ android:minHeight="50dp"
android:minResizeWidth="60dp"
- android:minResizeHeight="54dp"
+ android:minResizeHeight="50dp"
android:maxResizeHeight="207dp"
android:updatePeriodMillis="60000"
android:description="@string/people_tile_description"
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index bb1d972..cfef6cb 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -160,11 +160,18 @@
*
* @param separator Separator between different parts of the text
*/
- private CarrierTextManager(Context context, CharSequence separator, boolean showAirplaneMode,
- boolean showMissingSim, @Nullable WifiManager wifiManager,
- TelephonyManager telephonyManager, TelephonyListenerManager telephonyListenerManager,
- WakefulnessLifecycle wakefulnessLifecycle, @Main Executor mainExecutor,
- @Background Executor bgExecutor, KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ private CarrierTextManager(
+ Context context,
+ CharSequence separator,
+ boolean showAirplaneMode,
+ boolean showMissingSim,
+ @Nullable WifiManager wifiManager,
+ TelephonyManager telephonyManager,
+ TelephonyListenerManager telephonyListenerManager,
+ WakefulnessLifecycle wakefulnessLifecycle,
+ @Main Executor mainExecutor,
+ @Background Executor bgExecutor,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
mContext = context;
mIsEmergencyCallCapable = telephonyManager.isVoiceCapable();
@@ -633,12 +640,15 @@
private boolean mShowMissingSim;
@Inject
- public Builder(Context context, @Main Resources resources,
+ public Builder(
+ Context context,
+ @Main Resources resources,
@Nullable WifiManager wifiManager,
TelephonyManager telephonyManager,
TelephonyListenerManager telephonyListenerManager,
WakefulnessLifecycle wakefulnessLifecycle,
- @Main Executor mainExecutor, @Background Executor bgExecutor,
+ @Main Executor mainExecutor,
+ @Background Executor bgExecutor,
KeyguardUpdateMonitor keyguardUpdateMonitor) {
mContext = context;
mSeparator = resources.getString(
@@ -668,8 +678,8 @@
public CarrierTextManager build() {
return new CarrierTextManager(
mContext, mSeparator, mShowAirplaneMode, mShowMissingSim, mWifiManager,
- mTelephonyManager, mTelephonyListenerManager,
- mWakefulnessLifecycle, mMainExecutor, mBgExecutor, mKeyguardUpdateMonitor);
+ mTelephonyManager, mTelephonyListenerManager, mWakefulnessLifecycle,
+ mMainExecutor, mBgExecutor, mKeyguardUpdateMonitor);
}
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 561ea40..568bea0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -70,7 +70,7 @@
void onThemeChanged() {
TypedArray array = mContext.obtainStyledAttributes(new int[] {
- android.R.attr.textColor
+ android.R.attr.textColorPrimary
});
ColorStateList newTextColors = ColorStateList.valueOf(array.getColor(0, Color.RED));
array.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 8b68ae9..721b758 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -55,7 +55,7 @@
private static final String TAG = ImageWallpaper.class.getSimpleName();
// We delayed destroy render context that subsequent render requests have chance to cancel it.
// This is to avoid destroying then recreating render context in a very short time.
- private static final int DELAY_FINISH_RENDERING = 3000;
+ private static final int DELAY_FINISH_RENDERING = 1000;
private static final @android.annotation.NonNull RectF LOCAL_COLOR_BOUNDS =
new RectF(0, 0, 1, 1);
private static final boolean DEBUG = false;
@@ -107,12 +107,14 @@
private ImageWallpaperRenderer mRenderer;
private EglHelper mEglHelper;
private final Runnable mFinishRenderingTask = this::finishRendering;
+ private final Runnable mInitChoreographerTask = this::initChoreographerInternal;
private int mWidth = 1;
private int mHeight = 1;
private int mImgWidth = 1;
private int mImgHeight = 1;
private volatile float mDozeAmount;
private volatile boolean mNewDozeValue = false;
+ private volatile boolean mShouldScheduleFrame = false;
GLEngine() {
}
@@ -137,10 +139,7 @@
mWidth = window.width();
mMiniBitmap = null;
if (mWorker != null && mWorker.getThreadHandler() != null) {
- mWorker.getThreadHandler().post(() -> {
- updateMiniBitmap();
- Choreographer.getInstance().postFrameCallback(GLEngine.this);
- });
+ mWorker.getThreadHandler().post(this::updateMiniBitmap);
}
mDozeAmount = mStatusBarStateController.getDozeAmount();
@@ -222,15 +221,16 @@
@Override
public void onDestroy() {
mMiniBitmap = null;
+
+ mStatusBarStateController.removeCallback(this);
+
mWorker.getThreadHandler().post(() -> {
- Choreographer.getInstance().removeFrameCallback(this);
+ finishChoreographerInternal();
mRenderer.finish();
mRenderer = null;
mEglHelper.finish();
mEglHelper = null;
});
-
- mStatusBarStateController.removeCallback(this);
}
@Override
@@ -345,6 +345,8 @@
@Override
public void onDozeAmountChanged(float linear, float eased) {
+ initChoreographer();
+
mDozeAmount = linear;
mNewDozeValue = true;
}
@@ -355,8 +357,10 @@
postRender();
}
+ /**
+ * Important: this method should only be invoked from the ImageWallpaper (worker) Thread.
+ */
public void preRender() {
- // This method should only be invoked from worker thread.
Trace.beginSection("ImageWallpaper#preRender");
preRenderInternal();
Trace.endSection();
@@ -391,8 +395,10 @@
}
}
+ /**
+ * Important: this method should only be invoked from the ImageWallpaper (worker) Thread.
+ */
public void requestRender() {
- // This method should only be invoked from worker thread.
Trace.beginSection("ImageWallpaper#requestRender");
requestRenderInternal();
Trace.endSection();
@@ -416,8 +422,10 @@
}
}
+ /**
+ * Important: this method should only be invoked from the ImageWallpaper (worker) Thread.
+ */
public void postRender() {
- // This method should only be invoked from worker thread.
Trace.beginSection("ImageWallpaper#postRender");
scheduleFinishRendering();
Trace.endSection();
@@ -436,6 +444,7 @@
private void finishRendering() {
Trace.beginSection("ImageWallpaper#finishRendering");
+ finishChoreographerInternal();
if (mEglHelper != null) {
mEglHelper.destroyEglSurface();
mEglHelper.destroyEglContext();
@@ -443,6 +452,35 @@
Trace.endSection();
}
+ private void initChoreographer() {
+ if (!mWorker.getThreadHandler().hasCallbacks(mInitChoreographerTask)
+ && !mShouldScheduleFrame) {
+ mWorker.getThreadHandler().post(mInitChoreographerTask);
+ }
+ }
+
+ /**
+ * Subscribes the engine to listen to Choreographer frame events.
+ * Important: this method should only be invoked from the ImageWallpaper (worker) Thread.
+ */
+ private void initChoreographerInternal() {
+ if (!mShouldScheduleFrame) {
+ // Prepare EGL Context and Surface
+ preRender();
+ mShouldScheduleFrame = true;
+ Choreographer.getInstance().postFrameCallback(GLEngine.this);
+ }
+ }
+
+ /**
+ * Unsubscribe the engine from listening to Choreographer frame events.
+ * Important: this method should only be invoked from the ImageWallpaper (worker) Thread.
+ */
+ private void finishChoreographerInternal() {
+ mShouldScheduleFrame = false;
+ Choreographer.getInstance().removeFrameCallback(GLEngine.this);
+ }
+
private boolean needSupportWideColorGamut() {
return mRenderer.isWcgContent();
}
@@ -469,7 +507,10 @@
drawFrame();
mNewDozeValue = false;
}
- Choreographer.getInstance().postFrameCallback(this);
+
+ if (mShouldScheduleFrame) {
+ Choreographer.getInstance().postFrameCallback(this);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index 2569f7c..f7beaf1 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -26,7 +26,6 @@
import android.util.Log;
import android.util.Slog;
import android.view.Gravity;
-import android.view.View;
import android.view.WindowManager;
/**
@@ -98,8 +97,8 @@
private final Handler mHandler;
private int mGravity;
- private View mView;
- private View mNextView;
+ private WirelessChargingLayout mView;
+ private WirelessChargingLayout mNextView;
private WindowManager mWM;
private Callback mCallback;
@@ -112,7 +111,7 @@
mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
final WindowManager.LayoutParams params = mParams;
- params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.height = WindowManager.LayoutParams.MATCH_PARENT;
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.format = PixelFormat.TRANSLUCENT;
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index e8407f0..ce0b514 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -20,6 +20,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.drawable.Animatable;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -29,8 +30,10 @@
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.charging.ChargingRippleView;
import java.text.NumberFormat;
@@ -38,7 +41,9 @@
* @hide
*/
public class WirelessChargingLayout extends FrameLayout {
- public final static int UNKNOWN_BATTERY_LEVEL = -1;
+ public static final int UNKNOWN_BATTERY_LEVEL = -1;
+ private static final long RIPPLE_ANIMATION_DURATION = 2000;
+ private ChargingRippleView mRippleView;
public WirelessChargingLayout(Context context) {
super(context);
@@ -120,6 +125,8 @@
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
+ mRippleView = findViewById(R.id.wireless_charging_ripple);
+
if (!showTransmittingBatteryLevel) {
chargingAnimation.start();
animatorSet.start();
@@ -195,4 +202,21 @@
animatorSetTransmitting.start();
animatorSetIcon.start();
}
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (mRippleView != null) {
+ int width = getMeasuredWidth();
+ int height = getMeasuredHeight();
+ mRippleView.setColor(
+ Utils.getColorAttr(mRippleView.getContext(),
+ android.R.attr.colorAccent).getDefaultColor());
+ mRippleView.setOrigin(new PointF(width / 2, height / 2));
+ mRippleView.setRadius(Math.max(width, height) * 0.5f);
+ mRippleView.setDuration(RIPPLE_ANIMATION_DURATION);
+ mRippleView.startRipple();
+ }
+
+ super.onLayout(changed, left, top, right, bottom);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index c7ad3e8..47b41f5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -37,6 +37,7 @@
public static final int GENERIC = 7;
public static final int BOUNCER_UNLOCK = 8;
public static final int PULSE_EXPAND = 9;
+ public static final int BRIGHTNESS_SLIDER = 10;
@IntDef({
QUICK_SETTINGS,
@@ -48,7 +49,8 @@
RIGHT_AFFORDANCE,
GENERIC,
BOUNCER_UNLOCK,
- PULSE_EXPAND
+ PULSE_EXPAND,
+ BRIGHTNESS_SLIDER
})
@Retention(RetentionPolicy.SOURCE)
public @interface InteractionType {}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 5a9c386..80d7863 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -148,6 +148,10 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
+ if (interactionType == Classifier.BRIGHTNESS_SLIDER) {
+ return Result.passed(0);
+ }
+
return !getPassedFlingThreshold() ? falsed(0.5, getReason()) : Result.passed(0.5);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index ac330f0..6f80010 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -17,6 +17,7 @@
package com.android.systemui.classifier;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import android.provider.DeviceConfig;
@@ -115,7 +116,7 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
- if (interactionType == QUICK_SETTINGS) {
+ if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index 427c2ef..f665565 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -18,6 +18,7 @@
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
@@ -45,6 +46,7 @@
boolean up = isUp();
boolean right = isRight();
+ double confidence = 1;
boolean wrongDirection = true;
switch (interactionType) {
case QUICK_SETTINGS:
@@ -52,6 +54,11 @@
case NOTIFICATION_DRAG_DOWN:
wrongDirection = !vertical || up;
break;
+ case BRIGHTNESS_SLIDER:
+ confidence = 0; // Owners may return to original brightness.
+ // A more sophisticated thing to do here would be to look at the size of the
+ // vertical change relative to the screen size. _Some_ amount of vertical
+ // change should be expected.
case NOTIFICATION_DISMISS:
wrongDirection = vertical;
break;
@@ -70,7 +77,7 @@
break;
}
- return wrongDirection ? falsed(1, getReason(interactionType)) : Result.passed(0.5);
+ return wrongDirection ? falsed(confidence, getReason(interactionType)) : Result.passed(0.5);
}
private String getReason(int interactionType) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index 1d413af..d9197ef 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -20,6 +20,7 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import android.graphics.Point;
import android.provider.DeviceConfig;
@@ -87,6 +88,10 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
+ if (interactionType == BRIGHTNESS_SLIDER) {
+ return Result.passed(0);
+ }
+
List<MotionEvent> motionEvents = getRecentMotionEvents();
// Rotate horizontal gestures to be horizontal between their first and last point.
// Rotate vertical gestures to be vertical between their first and last point.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 52c9f16..b7f6e2b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -186,7 +186,7 @@
new TriggerSensor(
findSensorWithType(config.quickPickupSensorType()),
Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
- false /* setting default */,
+ true /* setting default */,
config.quickPickupSensorEnabled(KeyguardUpdateMonitor.getCurrentUser())
&& udfpsEnrolled,
DozeLog.REASON_SENSOR_QUICK_PICKUP,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 04b4670..aa66b75 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -531,9 +531,13 @@
Assert.isMainThread();
mDozeHost.extendPulse(reason);
+ // we can't determine the dozing state if we're currently transitioning
+ final DozeMachine.State dozeState =
+ mMachine.isExecutingTransition() ? null : mMachine.getState();
+
// When already pulsing we're allowed to show the wallpaper directly without
// requesting a new pulse.
- if (mMachine.getState() == DozeMachine.State.DOZE_PULSING
+ if (dozeState == DozeMachine.State.DOZE_PULSING
&& reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
mMachine.requestState(DozeMachine.State.DOZE_PULSING_BRIGHT);
return;
@@ -541,8 +545,7 @@
if (mPulsePending || !mAllowPulseTriggers || !canPulse()) {
if (mAllowPulseTriggers) {
- mDozeLog.tracePulseDropped(mPulsePending, mMachine.getState(),
- mDozeHost.isPulsingBlocked());
+ mDozeLog.tracePulseDropped(mPulsePending, dozeState, mDozeHost.isPulsingBlocked());
}
runIfNotNull(onPulseSuppressedListener);
return;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
index 073586e..6fbf81c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
@@ -44,12 +44,12 @@
* previous example, do the following:
*
* {@code
- * $ adb shell device_config put systemui flag_foo_bar_baz true
+ * $ adb shell setprop persist.systemui.flag_foo_bar_baz 1
* }
*
* Note that all storage keys begin with "flag_", even if their associated resId does not.
*
- * Calls to this class should probably be wrapped by {@link FeatureFlags}.
+ * Calls to this class should probably be wrapped by a method in {@link FeatureFlags}.
*/
@SysUISingleton
public class FeatureFlagReader {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 9d43e0c..c8dfde1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -209,6 +209,8 @@
private @TransitionMode int mNavigationBarMode;
private ContentResolver mContentResolver;
private boolean mAssistantAvailable;
+ private boolean mLongPressHomeEnabled;
+ private boolean mAssistantTouchGestureEnabled;
private int mDisabledFlags1;
private int mDisabledFlags2;
@@ -309,7 +311,7 @@
// Send the assistant availability upon connection
if (isConnected) {
- sendAssistantAvailability(mAssistantAvailable);
+ updateAssistantEntrypoints();
}
}
@@ -404,12 +406,7 @@
new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri) {
- boolean available = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
- if (mAssistantAvailable != available) {
- sendAssistantAvailability(available);
- mAssistantAvailable = available;
- }
+ updateAssistantEntrypoints();
}
};
@@ -531,6 +528,13 @@
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ updateAssistantEntrypoints();
if (savedState != null) {
mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
@@ -823,7 +827,7 @@
|| mNavigationBarView.getHomeButton().getCurrentView() == null) {
return;
}
- if (mHomeButtonLongPressDurationMs.isPresent()) {
+ if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) {
mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false);
mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
mNavigationBarView.getHomeButton().setOnLongClickListener(null);
@@ -845,6 +849,8 @@
pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
pw.println(" mCurrentRotation=" + mCurrentRotation);
pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
+ pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled);
+ pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
if (mNavigationBarView != null) {
pw.println(" mNavigationBarWindowState="
@@ -1206,9 +1212,11 @@
return true;
}
}
- mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
- mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
- });
+ if (mLongPressHomeEnabled) {
+ mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
+ mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
+ });
+ }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
@@ -1480,15 +1488,23 @@
| (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
}
- private void sendAssistantAvailability(boolean available) {
+ private void updateAssistantEntrypoints() {
+ mAssistantAvailable = mAssistManagerLazy.get()
+ .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ mLongPressHomeEnabled = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, 1) != 0;
+ mAssistantTouchGestureEnabled = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, 1) != 0;
if (mOverviewProxyService.getProxy() != null) {
try {
- mOverviewProxyService.getProxy().onAssistantAvailable(available
+ mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
+ && mAssistantTouchGestureEnabled
&& QuickStepContract.isGesturalMode(mNavBarMode));
} catch (RemoteException e) {
Log.w(TAG, "Unable to send assistant availability data to launcher");
}
}
+ reconfigureHomeLongClick();
}
// ----- Methods that DisplayNavigationBarController talks to -----
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
index 62b9458..2d0d5cd 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
@@ -21,7 +21,6 @@
import android.view.View;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.FeatureFlags;
import java.util.function.Consumer;
@@ -32,12 +31,10 @@
public class NavigationBarOverlayController {
protected final Context mContext;
- protected final FeatureFlags mFeatureFlags;
@Inject
- public NavigationBarOverlayController(Context context, FeatureFlags featureFlags) {
+ public NavigationBarOverlayController(Context context) {
mContext = context;
- mFeatureFlags = featureFlags;
}
public Context getContext() {
@@ -45,7 +42,7 @@
}
public boolean isNavigationBarOverlayEnabled() {
- return mFeatureFlags.isNavigationBarOverlayEnabled();
+ return false;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 5bc1280..440c5ef 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -175,7 +175,7 @@
/** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key,
- int appWidgetId) {
+ int appWidgetId, Uri contactUri) {
// Write relevant persisted storage.
SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
Context.MODE_PRIVATE);
@@ -186,27 +186,24 @@
widgetEditor.apply();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sp.edit();
- editor.putString(String.valueOf(appWidgetId), key.getShortcutId());
+ String contactUriString = contactUri == null ? EMPTY_STRING : contactUri.toString();
+ editor.putString(String.valueOf(appWidgetId), contactUriString);
// Don't overwrite existing widgets with the same key.
- Set<String> storedWidgetIds = new HashSet<>(
- sp.getStringSet(key.toString(), new HashSet<>()));
- storedWidgetIds.add(String.valueOf(appWidgetId));
- editor.putStringSet(key.toString(), storedWidgetIds);
+ addAppWidgetIdForKey(sp, editor, appWidgetId, key.toString());
+ addAppWidgetIdForKey(sp, editor, appWidgetId, contactUriString);
editor.apply();
}
/** Removes stored data when tile is deleted. */
public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key,
- int widgetId) {
+ int widgetId, String contactUriString) {
// Delete widgetId mapping to key.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sp.edit();
- Set<String> storedWidgetIds = new HashSet<>(
- sp.getStringSet(key.toString(), new HashSet<>()));
- storedWidgetIds.remove(String.valueOf(widgetId));
- editor.putStringSet(key.toString(), storedWidgetIds);
editor.remove(String.valueOf(widgetId));
+ removeAppWidgetIdForKey(sp, editor, widgetId, key.toString());
+ removeAppWidgetIdForKey(sp, editor, widgetId, contactUriString);
editor.apply();
// Delete all data specifically mapped to widgetId.
@@ -219,6 +216,23 @@
widgetEditor.apply();
}
+ private static void addAppWidgetIdForKey(SharedPreferences sp, SharedPreferences.Editor editor,
+ int widgetId, String storageKey) {
+ Set<String> storedWidgetIdsByKey = new HashSet<>(
+ sp.getStringSet(storageKey, new HashSet<>()));
+ storedWidgetIdsByKey.add(String.valueOf(widgetId));
+ editor.putStringSet(storageKey, storedWidgetIdsByKey);
+ }
+
+ private static void removeAppWidgetIdForKey(SharedPreferences sp,
+ SharedPreferences.Editor editor,
+ int widgetId, String storageKey) {
+ Set<String> storedWidgetIds = new HashSet<>(
+ sp.getStringSet(storageKey, new HashSet<>()));
+ storedWidgetIds.remove(String.valueOf(widgetId));
+ editor.putStringSet(storageKey, storedWidgetIds);
+ }
+
/** Augments a single {@link PeopleSpaceTile} with notification content, if one is present. */
public static PeopleSpaceTile augmentSingleTileFromVisibleNotifications(Context context,
PeopleSpaceTile tile, NotificationEntryManager notificationEntryManager) {
@@ -256,7 +270,7 @@
PeopleSpaceTile tile, Map<PeopleTileKey, NotificationEntry> visibleNotifications) {
PeopleTileKey key = new PeopleTileKey(
tile.getId(), getUserId(tile), tile.getPackageName());
-
+ // TODO: Match missed calls with matching Uris in addition to keys.
if (!visibleNotifications.containsKey(key)) {
if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key.toString());
return tile;
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 776e8a2..5be2d4a 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,17 +16,23 @@
package com.android.systemui.people.widget;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.app.Notification.CATEGORY_MISSED_CALL;
+import static android.app.Notification.EXTRA_PEOPLE_LIST;
+
import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
import static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotification;
+import static com.android.systemui.people.PeopleSpaceUtils.getMessagingStyleMessages;
import static com.android.systemui.people.PeopleSpaceUtils.getStoredWidgetIds;
import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView;
import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetViews;
import android.annotation.Nullable;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.app.Person;
@@ -39,6 +45,7 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.net.Uri;
import android.os.Bundle;
@@ -54,16 +61,20 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
+import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Dependency;
import com.android.systemui.people.PeopleSpaceUtils;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import javax.inject.Inject;
@@ -83,11 +94,19 @@
private SharedPreferences mSharedPrefs;
private PeopleManager mPeopleManager;
private NotificationEntryManager mNotificationEntryManager;
+ private PackageManager mPackageManager;
public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
@GuardedBy("mLock")
public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener>
mListeners = new HashMap<>();
+ @GuardedBy("mLock")
+ // Map of notification key mapped to widget IDs previously updated by the contact Uri field.
+ // This is required because on notification removal, the contact Uri field is stripped and we
+ // only have the notification key to determine which widget IDs should be updated.
+ private Map<String, Set<String>> mNotificationKeyToWidgetIdsMatchedByUri = new HashMap<>();
+ private boolean mIsForTesting;
+
@Inject
public PeopleSpaceWidgetManager(Context context) {
if (DEBUG) Log.d(TAG, "constructor");
@@ -99,6 +118,7 @@
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
mPeopleManager = mContext.getSystemService(PeopleManager.class);
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
+ mPackageManager = mContext.getPackageManager();
}
/**
@@ -108,12 +128,15 @@
protected void setAppWidgetManager(
AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager,
PeopleManager peopleManager, LauncherApps launcherApps,
- NotificationEntryManager notificationEntryManager) {
+ NotificationEntryManager notificationEntryManager, PackageManager packageManager,
+ boolean isForTesting) {
mAppWidgetManager = appWidgetManager;
mIPeopleManager = iPeopleManager;
mPeopleManager = peopleManager;
mLauncherApps = launcherApps;
mNotificationEntryManager = notificationEntryManager;
+ mPackageManager = packageManager;
+ mIsForTesting = isForTesting;
}
/**
@@ -222,6 +245,16 @@
public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction) {
if (DEBUG) Log.d(TAG, "updateWidgetsWithNotificationChanged called");
+ if (mIsForTesting) {
+ updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction);
+ return;
+ }
+ ThreadUtils.postOnBackgroundThread(
+ () -> updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction));
+ }
+
+ private void updateWidgetsWithNotificationChangedInBackground(StatusBarNotification sbn,
+ PeopleSpaceUtils.NotificationAction action) {
try {
String sbnShortcutId = sbn.getShortcutId();
if (sbnShortcutId == null) {
@@ -235,23 +268,175 @@
Log.d(TAG, "No app widget ids returned");
return;
}
+ PeopleTileKey key = new PeopleTileKey(
+ sbnShortcutId,
+ sbn.getUser().getIdentifier(),
+ sbn.getPackageName());
+ if (!key.isValid()) {
+ Log.d(TAG, "Invalid key");
+ return;
+ }
synchronized (mLock) {
- PeopleTileKey key = new PeopleTileKey(
- sbnShortcutId,
- UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier(),
- sbn.getPackageName());
- Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, key);
- for (String widgetIdString : storedWidgetIds) {
- int widgetId = Integer.parseInt(widgetIdString);
- if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
- updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId);
- }
+ // First, update People Tiles associated with the Notification's package/shortcut.
+ Set<String> tilesUpdatedByKey = getStoredWidgetIds(mSharedPrefs, key);
+ updateWidgetIdsForNotificationAction(tilesUpdatedByKey, sbn, action);
+
+ // Then, update People Tiles across other packages that use the same Uri.
+ updateTilesByUri(key, sbn, action);
}
} catch (Exception e) {
Log.e(TAG, "Exception: " + e);
}
}
+ /** Updates {@code widgetIdsToUpdate} with {@code action}. */
+ private void updateWidgetIdsForNotificationAction(Set<String> widgetIdsToUpdate,
+ StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction action) {
+ for (String widgetIdString : widgetIdsToUpdate) {
+ int widgetId = Integer.parseInt(widgetIdString);
+ PeopleSpaceTile storedTile = getTileForExistingWidget(widgetId);
+ if (storedTile == null) {
+ if (DEBUG) Log.d(TAG, "Could not find stored tile for notification");
+ continue;
+ }
+ if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
+ updateStorageAndViewWithNotificationData(sbn, action, widgetId,
+ storedTile);
+ }
+ }
+
+ /**
+ * Updates tiles with matched Uris, dependent on the {@code action}.
+ *
+ * <p>If the notification was added, adds the notification based on the contact Uri within
+ * {@code sbn}.
+ * <p>If the notification was removed, removes the notification based on the in-memory map of
+ * widgets previously updated by Uri (since the contact Uri is stripped from the {@code sbn}).
+ */
+ private void updateTilesByUri(PeopleTileKey key, StatusBarNotification sbn,
+ PeopleSpaceUtils.NotificationAction action) {
+ if (action.equals(PeopleSpaceUtils.NotificationAction.POSTED)) {
+ Set<String> widgetIdsUpdatedByUri = supplementTilesByUri(sbn, action, key);
+ if (widgetIdsUpdatedByUri != null && !widgetIdsUpdatedByUri.isEmpty()) {
+ if (DEBUG) Log.d(TAG, "Added due to uri: " + widgetIdsUpdatedByUri);
+ mNotificationKeyToWidgetIdsMatchedByUri.put(sbn.getKey(), widgetIdsUpdatedByUri);
+ }
+ } else {
+ // Remove the notification on any widgets where the notification was added
+ // purely based on the Uri.
+ Set<String> widgetsPreviouslyUpdatedByUri =
+ mNotificationKeyToWidgetIdsMatchedByUri.remove(sbn.getKey());
+ if (widgetsPreviouslyUpdatedByUri != null && !widgetsPreviouslyUpdatedByUri.isEmpty()) {
+ if (DEBUG) Log.d(TAG, "Remove due to uri: " + widgetsPreviouslyUpdatedByUri);
+ updateWidgetIdsForNotificationAction(widgetsPreviouslyUpdatedByUri, sbn,
+ action);
+ }
+ }
+ }
+
+ /**
+ * Retrieves from storage any tiles with the same contact Uri as linked via the {@code sbn}.
+ * Supplements the tiles with the notification content only if they still have {@link
+ * android.Manifest.permission.READ_CONTACTS} permission.
+ */
+ @Nullable
+ private Set<String> supplementTilesByUri(StatusBarNotification sbn,
+ PeopleSpaceUtils.NotificationAction notificationAction, PeopleTileKey key) {
+ if (!shouldMatchNotificationByUri(sbn)) {
+ if (DEBUG) Log.d(TAG, "Should not supplement conversation");
+ return null;
+ }
+
+ // Try to get the Contact Uri from the Missed Call notification directly.
+ String contactUri = getContactUri(sbn);
+ if (contactUri == null) {
+ if (DEBUG) Log.d(TAG, "No contact uri");
+ return null;
+ }
+
+ // Supplement any tiles with the same Uri.
+ Set<String> storedWidgetIdsByUri =
+ new HashSet<>(mSharedPrefs.getStringSet(contactUri, new HashSet<>()));
+ if (storedWidgetIdsByUri.isEmpty()) {
+ if (DEBUG) Log.d(TAG, "No tiles for contact");
+ return null;
+ }
+
+ if (mPackageManager.checkPermission(READ_CONTACTS,
+ sbn.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
+ if (DEBUG) Log.d(TAG, "Notifying app missing permissions");
+ return null;
+ }
+
+ Set<String> widgetIdsUpdatedByUri = new HashSet<>();
+ for (String widgetIdString : storedWidgetIdsByUri) {
+ int widgetId = Integer.parseInt(widgetIdString);
+ PeopleSpaceTile storedTile = getTileForExistingWidget(widgetId);
+ // Don't update a widget already updated.
+ if (key.equals(new PeopleTileKey(storedTile))) {
+ continue;
+ }
+ if (storedTile == null || mPackageManager.checkPermission(READ_CONTACTS,
+ storedTile.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
+ if (DEBUG) Log.d(TAG, "Cannot supplement tile: " + storedTile.getUserName());
+ continue;
+ }
+ if (DEBUG) Log.d(TAG, "Adding notification by uri: " + sbn.getKey());
+ updateStorageAndViewWithNotificationData(sbn, notificationAction,
+ widgetId, storedTile);
+ widgetIdsUpdatedByUri.add(String.valueOf(widgetId));
+ }
+ return widgetIdsUpdatedByUri;
+ }
+
+ /**
+ * Try to retrieve a valid Uri via {@code sbn}, falling back to the {@code
+ * contactUriFromShortcut} if valid.
+ */
+ @Nullable
+ private String getContactUri(StatusBarNotification sbn) {
+ // First, try to get a Uri from the Person directly set on the Notification.
+ ArrayList<Person> people = sbn.getNotification().extras.getParcelableArrayList(
+ EXTRA_PEOPLE_LIST);
+ if (people != null && people.get(0) != null) {
+ String contactUri = people.get(0).getUri();
+ if (contactUri != null && !contactUri.isEmpty()) {
+ return contactUri;
+ }
+ }
+
+ // Then, try to get a Uri from the Person set on the Notification message.
+ List<Notification.MessagingStyle.Message> messages =
+ getMessagingStyleMessages(sbn.getNotification());
+ if (messages != null && !messages.isEmpty()) {
+ Notification.MessagingStyle.Message message = messages.get(0);
+ Person sender = message.getSenderPerson();
+ if (sender != null && sender.getUri() != null && !sender.getUri().isEmpty()) {
+ return sender.getUri();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns whether a notification should be matched to other Tiles by Uri.
+ *
+ * <p>Currently only matches missed calls.
+ */
+ private boolean shouldMatchNotificationByUri(StatusBarNotification sbn) {
+ Notification notification = sbn.getNotification();
+ if (notification == null) {
+ if (DEBUG) Log.d(TAG, "Notification is null");
+ return false;
+ }
+ if (!Objects.equals(notification.category, CATEGORY_MISSED_CALL)) {
+ if (DEBUG) Log.d(TAG, "Not missed call");
+ return false;
+ }
+ return true;
+ }
+
/**
* Update the tiles associated with the incoming conversation update.
*/
@@ -309,16 +494,11 @@
private void updateStorageAndViewWithNotificationData(
StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction,
- int appWidgetId) {
- PeopleSpaceTile storedTile = getTileForExistingWidget(appWidgetId);
- if (storedTile == null) {
- if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
- return;
- }
+ int appWidgetId, PeopleSpaceTile storedTile) {
if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
storedTile = augmentTileFromNotification(mContext, storedTile, sbn);
- } else {
+ } else if (storedTile.getNotificationKey().equals(sbn.getKey())) {
if (DEBUG) {
Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
}
@@ -440,7 +620,8 @@
synchronized (mLock) {
if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getId());
PeopleTileKey key = new PeopleTileKey(tile);
- PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId);
+ PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId,
+ tile.getContactUri());
}
try {
if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
@@ -496,6 +677,7 @@
// Retrieve storage needed for widget deletion.
PeopleTileKey key;
Set<String> storedWidgetIdsForKey;
+ String contactUriString;
synchronized (mLock) {
SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId),
Context.MODE_PRIVATE);
@@ -509,9 +691,11 @@
}
storedWidgetIdsForKey = new HashSet<>(
mSharedPrefs.getStringSet(key.toString(), new HashSet<>()));
+ contactUriString = mSharedPrefs.getString(String.valueOf(widgetId), null);
}
synchronized (mLock) {
- PeopleSpaceUtils.removeSharedPreferencesStorageForTile(mContext, key, widgetId);
+ PeopleSpaceUtils.removeSharedPreferencesStorageForTile(mContext, key, widgetId,
+ contactUriString);
}
// If another tile with the conversation is still stored, we need to keep the listener.
if (DEBUG) Log.d(TAG, "Stored widget IDs: " + storedWidgetIdsForKey.toString());
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index bdd37fc..9cd97ff8 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -33,8 +33,6 @@
private var iconMargin = 0
private var iconSize = 0
private var iconColor = 0
- private var defaultBackgroundColor = 0
- private var cameraBackgroundColor = 0
private lateinit var iconsContainer: LinearLayout
@@ -75,11 +73,6 @@
if (!privacyList.isEmpty()) {
generateContentDescription(builder)
setIcons(builder, iconsContainer)
- if (builder.types.contains(PrivacyType.TYPE_CAMERA)) {
- iconsContainer.background.setTint(cameraBackgroundColor)
- } else {
- iconsContainer.background.setTint(defaultBackgroundColor)
- }
} else {
iconsContainer.removeAllViews()
}
@@ -99,8 +92,6 @@
.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
iconColor =
Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
- defaultBackgroundColor = context.getColor(R.color.privacy_circle_microphone_location)
- cameraBackgroundColor = context.getColor(R.color.privacy_circle_camera)
val padding = context.resources
.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index b8823e1..ee0b0c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -65,12 +65,14 @@
private final QuickQSPanel mQuickQsPanel;
private final QSPanelController mQsPanelController;
private final QuickQSPanelController mQuickQSPanelController;
+ private final QuickStatusBarHeader mQuickStatusBarHeader;
private final QSSecurityFooter mSecurityFooter;
private final QS mQs;
private PagedTileLayout mPagedLayout;
private boolean mOnFirstPage = true;
+ private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private TouchAnimator mFirstPageAnimator;
private TouchAnimator mFirstPageDelayedAnimator;
private TouchAnimator mTranslationXAnimator;
@@ -98,19 +100,22 @@
private final FeatureFlags mFeatureFlags;
@Inject
- public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanelController qsPanelController,
+ public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader,
+ QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags, QSExpansionPathInterpolator qsExpansionPathInterpolator) {
mQs = qs;
mQuickQsPanel = quickPanel;
mQsPanelController = qsPanelController;
mQuickQSPanelController = quickQSPanelController;
+ mQuickStatusBarHeader = quickStatusBarHeader;
mSecurityFooter = securityFooter;
mHost = qsTileHost;
mExecutor = executor;
mTunerService = tunerService;
mFeatureFlags = featureFlags;
+ mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mHost.addCallback(this);
mQsPanelController.addOnAttachStateChangeListener(this);
qs.getView().addOnLayoutChangeListener(this);
@@ -252,7 +257,9 @@
if (count < tileLayout.getNumVisibleTiles()) {
getRelativePosition(loc1, quickTileView, view);
getRelativePosition(loc2, tileView, view);
- int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0;
+ int yOffset = qsSideLabelsEnabled
+ ? loc2[1] - loc1[1]
+ : mQuickStatusBarHeader.getOffsetTranslation();
// Move the quick tile right from its location to the new one.
View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView;
translationXBuilder.addFloat(v, "translationX", 0, xDiff);
@@ -266,8 +273,14 @@
translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
if (qsSideLabelsEnabled) {
- translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset);
- translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0);
+ // Offset the translation animation on the views
+ // (that goes from 0 to getOffsetTranslation)
+ int offsetWithQSBHTranslation =
+ yOffset - mQuickStatusBarHeader.getOffsetTranslation();
+ translationYBuilder.addFloat(quickTileView, "translationY", 0,
+ offsetWithQSBHTranslation);
+ translationYBuilder.addFloat(tileView, "translationY",
+ -offsetWithQSBHTranslation, 0);
if (mQQSTileHeightAnimator == null) {
mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
@@ -375,22 +388,23 @@
}
float px = 0;
- float py = 1;
if (tiles.size() <= 3) {
px = 1;
} else if (tiles.size() <= 6) {
px = .4f;
}
- PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, px, py);
- translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator());
- translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
+ mQSExpansionPathInterpolator.setControlX2(px);
+ translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
+ translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
mTranslationXAnimator = translationXBuilder.build();
mTranslationYAnimator = translationYBuilder.build();
if (mQQSTileHeightAnimator != null) {
- mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+ mQQSTileHeightAnimator.setInterpolator(
+ mQSExpansionPathInterpolator.getYInterpolator());
}
if (mOtherTilesExpandAnimator != null) {
- mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+ mOtherTilesExpandAnimator.setInterpolator(
+ mQSExpansionPathInterpolator.getYInterpolator());
}
}
mNonfirstPageAnimator = new TouchAnimator.Builder()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 05bc6e2..d54d3f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -140,9 +140,10 @@
}
private void updateDetailText() {
- mDetailDoneButton.setText(R.string.quick_settings_done);
- final int resId =
- mDetailAdapter != null ? mDetailAdapter.getSettingsText() : Resources.ID_NULL;
+ int resId = mDetailAdapter != null ? mDetailAdapter.getDoneText() : Resources.ID_NULL;
+ mDetailDoneButton.setText(
+ (resId != Resources.ID_NULL) ? resId : R.string.quick_settings_done);
+ resId = mDetailAdapter != null ? mDetailAdapter.getSettingsText() : Resources.ID_NULL;
mDetailSettingsButton.setText(
(resId != Resources.ID_NULL) ? resId : R.string.quick_settings_more_settings);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt b/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt
new file mode 100644
index 0000000..d351b89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.view.animation.Interpolator
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class QSExpansionPathInterpolator @Inject constructor() {
+
+ private var pathInterpolatorBuilder = PathInterpolatorBuilder(0f, 0f, 0f, 1f)
+ private var lastX = 0f
+ val xInterpolator: Interpolator
+ get() = pathInterpolatorBuilder.xInterpolator
+
+ val yInterpolator: Interpolator
+ get() = pathInterpolatorBuilder.yInterpolator
+
+ fun setControlX2(value: Float) {
+ if (value != lastX) {
+ lastX = value
+ pathInterpolatorBuilder = PathInterpolatorBuilder(0f, 0f, lastX, 1f)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
deleted file mode 100644
index c654621..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
+++ /dev/null
@@ -1,131 +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.qs
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import android.widget.FrameLayout
-import com.android.systemui.R
-
-/**
- * Container for the Next Alarm and Ringer status texts in [QuickStatusBarHeader].
- *
- * If both elements are visible, it splits the available space according to the following rules:
- * * If both views add up to less than the total space, they take all the space they need.
- * * If both views are larger than half the space, each view takes half the space.
- * * Otherwise, the smaller view takes the space it needs and the larger one takes all remaining
- * space.
- */
-class QSHeaderInfoLayout @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0,
- defStyleRes: Int = 0
-) : FrameLayout(context, attrs, defStyle, defStyleRes) {
-
- private lateinit var alarmContainer: View
- private lateinit var ringerContainer: View
- private lateinit var statusSeparator: View
- private val location = Location(0, 0)
-
- override fun onFinishInflate() {
- super.onFinishInflate()
- alarmContainer = findViewById(R.id.alarm_container)
- ringerContainer = findViewById(R.id.ringer_container)
- statusSeparator = findViewById(R.id.status_separator)
- }
-
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
- // At most one view is there
- if (statusSeparator.visibility == View.GONE) super.onLayout(changed, l, t, r, b)
- else {
- val layoutRTL = isLayoutRtl
- val width = r - l
- val height = b - t
- var offset = 0
-
- offset += alarmContainer.layoutView(width, height, offset, layoutRTL)
- offset += statusSeparator.layoutView(width, height, offset, layoutRTL)
- ringerContainer.layoutView(width, height, offset, layoutRTL)
- }
- }
-
- private fun View.layoutView(pWidth: Int, pHeight: Int, offset: Int, RTL: Boolean): Int {
- location.setLocationFromOffset(pWidth, offset, this.measuredWidth, RTL)
- layout(location.left, 0, location.right, pHeight)
- return this.measuredWidth
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(
- MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST),
- heightMeasureSpec)
- val width = MeasureSpec.getSize(widthMeasureSpec)
- // Once we measure the views, using as much space as they need, we need to remeasure them
- // assigning them their final width. This is because TextViews decide whether to MARQUEE
- // after onMeasure.
- if (statusSeparator.visibility != View.GONE) {
- val alarmWidth = alarmContainer.measuredWidth
- val separatorWidth = statusSeparator.measuredWidth
- val ringerWidth = ringerContainer.measuredWidth
- val availableSpace = MeasureSpec.getSize(width) - separatorWidth
- if (alarmWidth < availableSpace / 2) {
- measureChild(
- ringerContainer,
- MeasureSpec.makeMeasureSpec(
- Math.min(ringerWidth, availableSpace - alarmWidth),
- MeasureSpec.AT_MOST),
- heightMeasureSpec)
- } else if (ringerWidth < availableSpace / 2) {
- measureChild(alarmContainer,
- MeasureSpec.makeMeasureSpec(
- Math.min(alarmWidth, availableSpace - ringerWidth),
- MeasureSpec.AT_MOST),
- heightMeasureSpec)
- } else {
- measureChild(
- alarmContainer,
- MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST),
- heightMeasureSpec)
- measureChild(
- ringerContainer,
- MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST),
- heightMeasureSpec)
- }
- }
- setMeasuredDimension(width, measuredHeight)
- }
-
- private data class Location(var left: Int, var right: Int) {
- /**
- * Sets the [left] and [right] with the correct values for laying out the child, respecting
- * RTL. Only set the variable through here to prevent concurrency issues.
- * This is done to prevent allocation of [Pair] in [onLayout].
- */
- fun setLocationFromOffset(parentWidth: Int, offset: Int, width: Int, RTL: Boolean) {
- if (RTL) {
- left = parentWidth - offset - width
- right = parentWidth - offset
- } else {
- left = offset
- right = offset + width
- }
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index c794a21..95f7e20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -391,20 +391,13 @@
index++;
if (mSecurityFooter != null) {
- 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(mSecurityFooter, mHeaderContainer, 1 /* always in second place */);
+ switchToParent(mSecurityFooter, mHeaderContainer, 0);
} else {
- layoutParams.width = LayoutParams.WRAP_CONTENT;
- layoutParams.weight = 0;
switchToParent(mSecurityFooter, parent, index);
index++;
}
- mSecurityFooter.setLayoutParams(layoutParams);
}
if (mFooter != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 5e13fd3..2c5dbcd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -283,31 +283,17 @@
return mContext.getString(R.string.quick_settings_disclosure_named_vpn,
vpnName);
}
+ if (hasWorkProfile && isNetworkLoggingEnabled) {
+ return mContext.getString(
+ R.string.quick_settings_disclosure_managed_profile_network_activity);
+ }
if (isProfileOwnerOfOrganizationOwnedDevice) {
- if (isNetworkLoggingEnabled) {
- if (organizationName == null) {
- return mContext.getString(
- R.string.quick_settings_disclosure_management_monitoring);
- }
- return mContext.getString(
- R.string.quick_settings_disclosure_named_management_monitoring,
- organizationName);
- }
if (workProfileOrganizationName == null) {
return mContext.getString(R.string.quick_settings_disclosure_management);
}
return mContext.getString(R.string.quick_settings_disclosure_named_management,
workProfileOrganizationName);
}
- if (hasWorkProfile && isNetworkLoggingEnabled) {
- if (workProfileOrganizationName == null) {
- return mContext.getString(
- R.string.quick_settings_disclosure_managed_profile_monitoring);
- }
- return mContext.getString(
- R.string.quick_settings_disclosure_named_managed_profile_monitoring,
- workProfileOrganizationName);
- }
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 3e3451e..0a9c12f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -474,22 +474,21 @@
if (tiles.contains("internet") || tiles.contains("wifi") || tiles.contains("cell")) {
if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
if (!tiles.contains("internet")) {
- tiles.add("internet");
- }
- if (tiles.contains("wifi")) {
+ if (tiles.contains("wifi")) {
+ // Replace the WiFi with Internet, and remove the Cell
+ tiles.set(tiles.indexOf("wifi"), "internet");
+ tiles.remove("cell");
+ } else if (tiles.contains("cell")) {
+ // Replace the Cell with Internet
+ tiles.set(tiles.indexOf("cell"), "internet");
+ }
+ } else {
tiles.remove("wifi");
- }
- if (tiles.contains("cell")) {
tiles.remove("cell");
}
} else {
if (tiles.contains("internet")) {
- tiles.remove("internet");
- }
- if (!tiles.contains("wifi")) {
- tiles.add("wifi");
- }
- if (!tiles.contains("cell")) {
+ tiles.set(tiles.indexOf("internet"), "wifi");
tiles.add("cell");
}
}
@@ -513,6 +512,14 @@
&& GarbageMonitor.ADD_MEMORY_TILE_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
}
+ // TODO(b/174753536): Change the config file directly.
+ // Filter out unused tiles from the default QS config.
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ tiles.remove("cell");
+ tiles.remove("wifi");
+ } else {
+ tiles.remove("internet");
+ }
return tiles;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a0bf584..82ae2dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,15 +17,14 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.annotation.ColorInt;
-import android.app.AlarmManager.AlarmClockInfo;
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
+
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.media.AudioManager;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.Pair;
@@ -34,66 +33,46 @@
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
import android.widget.Space;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.qs.QSDetail.Callback;
+import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
+import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
-import java.util.Locale;
-import java.util.Objects;
-
/**
- * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
- * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
- * contents.
+ * View that contains the top-most bits of the QS panel (primarily the status bar with date, time,
+ * battery, carrier info and privacy icons) and also contains the {@link QuickQSPanel}.
*/
-public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwner {
-
- public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
+public class QuickStatusBarHeader extends FrameLayout {
private boolean mExpanded;
private boolean mQsDisabled;
+ private TouchAnimator mAlphaAnimator;
+ private TouchAnimator mTranslationAnimator;
+
protected QuickQSPanel mHeaderQsPanel;
- private TouchAnimator mStatusIconsAlphaAnimator;
- private TouchAnimator mHeaderTextContainerAlphaAnimator;
+ private View mDatePrivacyView;
+ private View mClockIconsView;
+ private View mContainer;
- private View mSystemIconsView;
- private View mQuickQsStatusIcons;
- private View mHeaderTextContainerView;
-
- private ImageView mNextAlarmIcon;
- /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
- private TextView mNextAlarmTextView;
- private View mNextAlarmContainer;
- private View mStatusSeparator;
- private ImageView mRingerModeIcon;
- private TextView mRingerModeTextView;
- private View mRingerContainer;
+ private View mQSCarriers;
private Clock mClockView;
- private OngoingPrivacyChip mPrivacyChip;
private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
- private TintedIconManager mTintedIconManager;
+ private StatusIconContainer mIconContainer;
- // Used for RingerModeTracker
- private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
+ private TintedIconManager mTintedIconManager;
+ private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private int mStatusBarPaddingTop = 0;
private int mRoundedCornerPadding = 0;
@@ -102,12 +81,26 @@
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
- private float mExpandedHeaderAlpha = 1.0f;
+ private float mClockIconsAlpha = 1.0f;
+ private float mDatePrivacyAlpha = 1.0f;
private float mKeyguardExpansionFraction;
private int mTextColorPrimary = Color.TRANSPARENT;
+ private int mTopViewMeasureHeight;
+
+ private final String mMobileSlotName;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
+ mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile);
+ }
+
+ /**
+ * How much the view containing the clock and QQS will translate down when QS is fully expanded.
+ *
+ * This matches the measured height of the view containing the date and privacy icons.
+ */
+ public int getOffsetTranslation() {
+ return mTopViewMeasureHeight;
}
@Override
@@ -115,19 +108,12 @@
super.onFinishInflate();
mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
- mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
- mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
+ mDatePrivacyView = findViewById(R.id.quick_status_bar_date_privacy);
+ mClockIconsView = findViewById(R.id.quick_qs_status_icons);
+ mQSCarriers = findViewById(R.id.carrier_group);
+ mContainer = findViewById(R.id.container);
+ mIconContainer = findViewById(R.id.statusIcons);
- // Views corresponding to the header info section (e.g. ringer and next alarm).
- mHeaderTextContainerView = findViewById(R.id.header_text_container);
- mStatusSeparator = findViewById(R.id.status_separator);
- mNextAlarmIcon = findViewById(R.id.next_alarm_icon);
- mNextAlarmTextView = findViewById(R.id.next_alarm_text);
- mNextAlarmContainer = findViewById(R.id.alarm_container);
- mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
- mRingerModeTextView = findViewById(R.id.ringer_mode_text);
- mRingerContainer = findViewById(R.id.ringer_container);
- mPrivacyChip = findViewById(R.id.privacy_chip);
mClockView = findViewById(R.id.clock);
mSpace = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
@@ -140,79 +126,34 @@
// QS will always show the estimate, and BatteryMeterView handles the case where
// it's unavailable or charging
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
- mRingerModeTextView.setSelected(true);
- mNextAlarmTextView.setSelected(true);
}
- void onAttach(TintedIconManager iconManager) {
+ void onAttach(TintedIconManager iconManager,
+ QSExpansionPathInterpolator qsExpansionPathInterpolator) {
mTintedIconManager = iconManager;
int fillColor = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.textColorPrimary);
// Set the correct tint for the status icons so they contrast
iconManager.setTint(fillColor);
- mNextAlarmIcon.setImageTintList(ColorStateList.valueOf(fillColor));
- mRingerModeIcon.setImageTintList(ColorStateList.valueOf(fillColor));
+
+ mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
+ updateAnimators();
}
public QuickQSPanel getHeaderQsPanel() {
return mHeaderQsPanel;
}
- void updateStatusText(int ringerMode, AlarmClockInfo nextAlarm, boolean zenOverridingRinger,
- boolean use24HourFormat) {
- boolean changed = updateRingerStatus(ringerMode, zenOverridingRinger)
- || updateAlarmStatus(nextAlarm, use24HourFormat);
-
- if (changed) {
- boolean alarmVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
- boolean ringerVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
- mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE
- : View.GONE);
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) {
+ mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight();
+ updateAnimators();
}
}
- private boolean updateRingerStatus(int ringerMode, boolean zenOverridingRinger) {
- boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
- CharSequence originalRingerText = mRingerModeTextView.getText();
-
- boolean ringerVisible = false;
- if (!zenOverridingRinger) {
- if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
- mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
- mRingerModeTextView.setText(R.string.qs_status_phone_vibrate);
- ringerVisible = true;
- } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
- mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
- mRingerModeTextView.setText(R.string.qs_status_phone_muted);
- ringerVisible = true;
- }
- }
- mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
- mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
- mRingerContainer.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
-
- return isOriginalVisible != ringerVisible ||
- !Objects.equals(originalRingerText, mRingerModeTextView.getText());
- }
-
- private boolean updateAlarmStatus(AlarmClockInfo nextAlarm, boolean use24HourFormat) {
- boolean isOriginalVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
- CharSequence originalAlarmText = mNextAlarmTextView.getText();
-
- boolean alarmVisible = false;
- if (nextAlarm != null) {
- alarmVisible = true;
- mNextAlarmTextView.setText(formatNextAlarm(nextAlarm, use24HourFormat));
- }
- mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
- mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
- mNextAlarmContainer.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
-
- return isOriginalVisible != alarmVisible ||
- !Objects.equals(originalAlarmText, mNextAlarmTextView.getText());
- }
-
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -225,40 +166,27 @@
updateResources();
}
- /**
- * The height of QQS should always be the status bar height + 128dp. This is normally easy, but
- * when there is a notch involved the status bar can remain a fixed pixel size.
- */
- private void updateMinimumHeight() {
- int sbHeight = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- int qqsHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_quick_header_panel_height);
-
- setMinimumHeight(sbHeight + qqsHeight);
- }
-
void updateResources() {
Resources resources = mContext.getResources();
- updateMinimumHeight();
mRoundedCornerPadding = resources.getDimensionPixelSize(
R.dimen.rounded_corner_content_padding);
mStatusBarPaddingTop = resources.getDimensionPixelSize(R.dimen.status_bar_padding_top);
- // Update height for a few views, especially due to landscape mode restricting space.
- mHeaderTextContainerView.getLayoutParams().height =
- resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
- mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
-
- mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize(
+ int qsOffsetHeight = resources.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
+
+ mDatePrivacyView.getLayoutParams().height =
+ Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight());
+ mDatePrivacyView.setLayoutParams(mDatePrivacyView.getLayoutParams());
+
+ mClockIconsView.getLayoutParams().height =
+ Math.max(qsOffsetHeight, mClockIconsView.getMinimumHeight());
+ mClockIconsView.setLayoutParams(mClockIconsView.getLayoutParams());
ViewGroup.LayoutParams lp = getLayoutParams();
if (mQsDisabled) {
- lp.height = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height);
+ lp.height = mClockIconsView.getLayoutParams().height;
} else {
lp.height = WRAP_CONTENT;
}
@@ -276,21 +204,45 @@
mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary,
mTextColorPrimary);
}
-
- updateStatusIconAlphaAnimator();
- updateHeaderTextContainerAlphaAnimator();
+ updateHeadersPadding();
+ updateAnimators();
}
- private void updateStatusIconAlphaAnimator() {
- mStatusIconsAlphaAnimator = new TouchAnimator.Builder()
- .addFloat(mQuickQsStatusIcons, "alpha", 1, 0, 0)
+ private void updateAnimators() {
+ updateAlphaAnimator();
+ int offset = mTopViewMeasureHeight;
+
+ mTranslationAnimator = new TouchAnimator.Builder()
+ .addFloat(mContainer, "translationY", 0, offset)
+ .setInterpolator(mQSExpansionPathInterpolator != null
+ ? mQSExpansionPathInterpolator.getYInterpolator()
+ : null)
.build();
}
- private void updateHeaderTextContainerAlphaAnimator() {
- mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
- .addFloat(mHeaderTextContainerView, "alpha", 0, 0, mExpandedHeaderAlpha)
- .build();
+ private void updateAlphaAnimator() {
+ StatusBarMobileView icon =
+ ((StatusBarMobileView) mIconContainer.getViewForSlot(mMobileSlotName));
+ TouchAnimator.Builder builder = new TouchAnimator.Builder()
+ .addFloat(mQSCarriers, "alpha", 0, 1)
+ .addFloat(mDatePrivacyView, "alpha", 0, mDatePrivacyAlpha);
+ if (icon != null) {
+ builder.addFloat(icon, "alpha", 1, 0);
+ builder.setListener(new TouchAnimator.ListenerAdapter() {
+ @Override
+ public void onAnimationAtEnd() {
+ icon.forceHidden(true);
+ icon.setVisibleState(STATE_HIDDEN);
+ }
+
+ @Override
+ public void onAnimationStarted() {
+ icon.forceHidden(false);
+ icon.setVisibleState(STATE_ICON);
+ }
+ });
+ }
+ mAlphaAnimator = builder.build();
}
/** */
@@ -312,25 +264,19 @@
public void setExpansion(boolean forceExpanded, float expansionFraction,
float panelTranslationY) {
final float keyguardExpansionFraction = forceExpanded ? 1f : expansionFraction;
- if (mStatusIconsAlphaAnimator != null) {
- mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
- }
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.setPosition(keyguardExpansionFraction);
+ }
+ if (mTranslationAnimator != null) {
+ mTranslationAnimator.setPosition(keyguardExpansionFraction);
+ }
+ // If forceExpanded (we are opening QS from lockscreen), the animators have been set to
+ // position = 1f.
if (forceExpanded) {
- // If the keyguard is showing, we want to offset the text so that it comes in at the
- // same time as the panel as it slides down.
- mHeaderTextContainerView.setTranslationY(panelTranslationY);
+ setTranslationY(panelTranslationY);
} else {
- mHeaderTextContainerView.setTranslationY(0f);
- }
-
- if (mHeaderTextContainerAlphaAnimator != null) {
- mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
- if (keyguardExpansionFraction > 0) {
- mHeaderTextContainerView.setVisibility(VISIBLE);
- } else {
- mHeaderTextContainerView.setVisibility(INVISIBLE);
- }
+ setTranslationY(0);
}
mKeyguardExpansionFraction = keyguardExpansionFraction;
@@ -341,28 +287,21 @@
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
mHeaderQsPanel.setDisabledByPolicy(disabled);
- mHeaderTextContainerView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
- mQuickQsStatusIcons.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+ mClockIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
updateResources();
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- // Handle padding of the clock
+ // Handle padding of the views
DisplayCutout cutout = insets.getDisplayCutout();
Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
cutout, getDisplay());
Pair<Integer, Integer> padding =
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
cutout, cornerCutoutPadding, -1);
- if (padding == null) {
- mSystemIconsView.setPaddingRelative(
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
- } else {
- mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
-
- }
+ mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
+ mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
boolean cornerCutout = cornerCutoutPadding != null
&& (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
@@ -380,13 +319,13 @@
mCutOutPaddingLeft = padding.first;
mCutOutPaddingRight = padding.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
- updateClockPadding();
+ updateHeadersPadding();
return super.onApplyWindowInsets(insets);
}
- private void updateClockPadding() {
- int clockPaddingLeft = 0;
- int clockPaddingRight = 0;
+ private void updateHeadersPadding() {
+ int paddingLeft = 0;
+ int paddingRight = 0;
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
int leftMargin = lp.leftMargin;
@@ -399,18 +338,22 @@
// if there's a cutout, let's use at least the rounded corner inset
int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding);
int contentMarginLeft = isLayoutRtl() ? mContentMarginEnd : mContentMarginStart;
- clockPaddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0);
+ paddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0);
}
if (mCutOutPaddingRight > 0) {
// if there's a cutout, let's use at least the rounded corner inset
int cutoutPadding = Math.max(mCutOutPaddingRight, mRoundedCornerPadding);
int contentMarginRight = isLayoutRtl() ? mContentMarginStart : mContentMarginEnd;
- clockPaddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0);
+ paddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0);
}
- mSystemIconsView.setPadding(clockPaddingLeft,
+ mDatePrivacyView.setPadding(paddingLeft,
mWaterfallTopInset + mStatusBarPaddingTop,
- clockPaddingRight,
+ paddingRight,
+ 0);
+ mClockIconsView.setPadding(paddingLeft,
+ mWaterfallTopInset + mStatusBarPaddingTop,
+ paddingRight,
0);
}
@@ -422,26 +365,6 @@
mHeaderQsPanel.setCallback(qsPanelCallback);
}
- private String formatNextAlarm(AlarmClockInfo info, boolean use24HourFormat) {
- if (info == null) {
- return "";
- }
- String skeleton = use24HourFormat ? "EHm" : "Ehma";
- String pattern = android.text.format.DateFormat
- .getBestDateTimePattern(Locale.getDefault(), skeleton);
- return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
- }
-
- public static float getColorIntensity(@ColorInt int color) {
- return color == Color.WHITE ? 0 : 1;
- }
-
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycle;
- }
-
/** */
public void setContentMargins(int marginStart, int marginEnd,
QuickQSPanelController quickQSPanelController) {
@@ -459,24 +382,39 @@
view.setLayoutParams(lp);
}
}
- updateClockPadding();
+ updateHeadersPadding();
}
+ /**
+ * When QS is scrolling, mClockIconsAlpha should scroll away and fade out.
+ *
+ * For a given scroll level, this method does the following:
+ * <ol>
+ * <li>Determine the alpha that {@code mClockIconsView} should have when the panel is fully
+ * expanded.</li>
+ * <li>Set the scroll of {@code mClockIconsView} to the same of {@code QSPanel}.</li>
+ * <li>Set the alpha of {@code mClockIconsView} to that determined by the expansion of
+ * the panel, interpolated between 1 (no expansion) and {@code mClockIconsAlpha} (fully
+ * expanded), matching the animator.</li>
+ * </ol>
+ *
+ * @param scrollY the scroll of the QSPanel container
+ */
public void setExpandedScrollAmount(int scrollY) {
// The scrolling of the expanded qs has changed. Since the header text isn't part of it,
// but would overlap content, we're fading it out.
float newAlpha = 1.0f;
- if (mHeaderTextContainerView.getHeight() > 0) {
- newAlpha = MathUtils.map(0, mHeaderTextContainerView.getHeight() / 2.0f, 1.0f, 0.0f,
+ if (mClockIconsView.getHeight() > 0) {
+ newAlpha = MathUtils.map(0, mClockIconsView.getHeight() / 2.0f, 1.0f, 0.0f,
scrollY);
newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha);
}
- mHeaderTextContainerView.setScrollY(scrollY);
- if (newAlpha != mExpandedHeaderAlpha) {
- mExpandedHeaderAlpha = newAlpha;
- mHeaderTextContainerView.setAlpha(MathUtils.lerp(0.0f, mExpandedHeaderAlpha,
+ mClockIconsView.setScrollY(scrollY);
+ if (newAlpha != mClockIconsAlpha) {
+ mClockIconsAlpha = newAlpha;
+ mClockIconsView.setAlpha(MathUtils.lerp(1.0f, mClockIconsAlpha,
mKeyguardExpansionFraction));
- updateHeaderTextContainerAlphaAnimator();
+ updateAlphaAnimator();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index b1689f6..3aafea98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -16,21 +16,13 @@
package com.android.systemui.qs;
-import android.app.AlarmManager.AlarmClockInfo;
import android.content.Intent;
-import android.media.AudioManager;
import android.os.Bundle;
import android.provider.AlarmClock;
-import android.provider.Settings;
-import android.service.notification.ZenModeConfig;
-import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
@@ -47,15 +39,9 @@
import com.android.systemui.privacy.logging.PrivacyLogger;
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.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeController.Callback;
-import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.ViewController;
import java.util.ArrayList;
@@ -70,75 +56,29 @@
class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
private static final String TAG = "QuickStatusBarHeader";
- 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 QSCarrierGroupController mQSCarrierGroupController;
private final QuickQSPanelController mHeaderQsPanelController;
- private final LifecycleRegistry mLifecycle;
private final OngoingPrivacyChip mPrivacyChip;
private final Clock mClockView;
- private final View mNextAlarmContainer;
- private final View mRingerContainer;
- private final QSTileHost mQSTileHost;
private final StatusBarIconController mStatusBarIconController;
private final DemoModeController mDemoModeController;
- private final UserTracker mUserTracker;
private final StatusIconContainer mIconContainer;
private final StatusBarIconController.TintedIconManager mIconManager;
private final DemoMode mDemoModeReceiver;
private final PrivacyLogger mPrivacyLogger;
private final PrivacyDialogController mPrivacyDialogController;
+ private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private boolean mListening;
- private AlarmClockInfo mNextAlarm;
private boolean mMicCameraIndicatorsEnabled;
private boolean mLocationIndicatorsEnabled;
private boolean mPrivacyChipLogged;
private SysuiColorExtractor mColorExtractor;
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
- private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
-
- private final ZenModeController.Callback mZenModeControllerCallback = new Callback() {
- @Override
- public void onZenChanged(int zen) {
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- }
-
- @Override
- public void onConfigChanged(ZenModeConfig config) {
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- }
- };
-
- private boolean use24HourFormat() {
- return android.text.format.DateFormat.is24HourFormat(
- mView.getContext(), mUserTracker.getUserId());
-
- }
-
- private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() {
- @Override
- public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
- mNextAlarm = nextAlarm;
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- }
- };
-
- private final LifecycleOwner mLifecycleOwner = new LifecycleOwner() {
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycle;
- }
- };
private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
@Override
@@ -176,63 +116,43 @@
if (v == mClockView) {
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
- } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) {
- if (mNextAlarm.getShowIntent() != null) {
- mActivityStarter.postStartActivityDismissingKeyguard(
- mNextAlarm.getShowIntent());
- } else {
- Log.d(TAG, "No PendingIntent for next alarm. Using default intent");
- mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
- AlarmClock.ACTION_SHOW_ALARMS), 0);
- }
} else if (v == mPrivacyChip) {
// If the privacy chip is visible, it means there were some indicators
mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
mPrivacyDialogController.showDialog(getContext());
- } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
- mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
- Settings.ACTION_SOUND_SETTINGS), 0);
}
}
};
@Inject
QuickStatusBarHeaderController(QuickStatusBarHeader view,
- ZenModeController zenModeController, NextAlarmController nextAlarmController,
- PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker,
+ PrivacyItemController privacyItemController,
ActivityStarter activityStarter, UiEventLogger uiEventLogger,
- QSTileHost qsTileHost, StatusBarIconController statusBarIconController,
+ StatusBarIconController statusBarIconController,
DemoModeController demoModeController,
- UserTracker userTracker, QuickQSPanelController quickQSPanelController,
+ QuickQSPanelController quickQSPanelController,
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
PrivacyLogger privacyLogger,
SysuiColorExtractor colorExtractor,
- PrivacyDialogController privacyDialogController) {
+ PrivacyDialogController privacyDialogController,
+ QSExpansionPathInterpolator qsExpansionPathInterpolator) {
super(view);
- mZenModeController = zenModeController;
- mNextAlarmController = nextAlarmController;
mPrivacyItemController = privacyItemController;
- mRingerModeTracker = ringerModeTracker;
mActivityStarter = activityStarter;
mUiEventLogger = uiEventLogger;
- mQSTileHost = qsTileHost;
mStatusBarIconController = statusBarIconController;
mDemoModeController = demoModeController;
- mUserTracker = userTracker;
- mLifecycle = new LifecycleRegistry(mLifecycleOwner);
mHeaderQsPanelController = quickQSPanelController;
mPrivacyLogger = privacyLogger;
mPrivacyDialogController = privacyDialogController;
+ mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
.build();
-
mPrivacyChip = mView.findViewById(R.id.privacy_chip);
- mNextAlarmContainer = mView.findViewById(R.id.alarm_container);
mClockView = mView.findViewById(R.id.clock);
- mRingerContainer = mView.findViewById(R.id.ringer_container);
mIconContainer = mView.findViewById(R.id.statusIcons);
mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer);
@@ -247,15 +167,7 @@
@Override
protected void onViewAttached() {
- mRingerModeTracker.getRingerModeInternal().observe(mLifecycleOwner, ringer -> {
- mRingerMode = ringer;
- mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
- use24HourFormat());
- });
-
mClockView.setOnClickListener(mOnClickListener);
- mNextAlarmContainer.setOnClickListener(mOnClickListener);
- mRingerContainer.setOnClickListener(mOnClickListener);
mPrivacyChip.setOnClickListener(mOnClickListener);
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
@@ -268,18 +180,15 @@
setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
- mView.onAttach(mIconManager);
+ mView.onAttach(mIconManager, mQSExpansionPathInterpolator);
mDemoModeController.addCallback(mDemoModeReceiver);
}
@Override
protected void onViewDetached() {
- mRingerModeTracker.getRingerModeInternal().removeObservers(mLifecycleOwner);
mClockView.setOnClickListener(null);
mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
- mNextAlarmContainer.setOnClickListener(null);
- mRingerContainer.setOnClickListener(null);
mPrivacyChip.setOnClickListener(null);
mStatusBarIconController.removeIconGroup(mIconManager);
mDemoModeController.removeCallback(mDemoModeReceiver);
@@ -304,17 +213,11 @@
}
if (listening) {
- mZenModeController.addCallback(mZenModeControllerCallback);
- mNextAlarmController.addCallback(mNextAlarmChangeCallback);
- mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
// Get the most up to date info
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
mPrivacyItemController.addCallback(mPICCallback);
} else {
- mZenModeController.removeCallback(mZenModeControllerCallback);
- mNextAlarmController.removeCallback(mNextAlarmChangeCallback);
- mLifecycle.setCurrentState(Lifecycle.State.CREATED);
mPrivacyItemController.removeCallback(mPICCallback);
mPrivacyChipLogged = false;
}
@@ -357,11 +260,6 @@
return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
}
- private boolean isZenOverridingRinger() {
- return ZenModeConfig.isZenOverridingRinger(mZenModeController.getZen(),
- mZenModeController.getConsolidatedPolicy());
- }
-
public void setContentMargins(int contentPaddingStart, int contentPaddingEnd) {
mView.setContentMargins(contentPaddingStart, contentPaddingEnd, mHeaderQsPanelController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 0ebadfd..d1deeca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.util.AttributeSet
+import com.android.systemui.R
open class SideLabelTileLayout(
context: Context,
@@ -26,7 +27,7 @@
override fun updateResources(): Boolean {
return super.updateResources().also {
- mMaxAllowedRows = 4
+ mMaxAllowedRows = context.resources.getInteger(R.integer.quick_settings_max_rows)
}
}
@@ -44,4 +45,19 @@
val row = index / mColumns
return getRowTop(row)
}
+
+ override fun updateMaxRows(allowedHeight: Int, tilesCount: Int): Boolean {
+ val previousRows = mRows
+ mRows = mMaxAllowedRows
+ // We want at most mMaxAllowedRows, but it could be that we don't have enough tiles to fit
+ // that many rows. In that case, we want
+ // `tilesCount = (mRows - 1) * mColumns + X`
+ // where X is some remainder between 1 and `mColumns - 1`
+ // Adding `mColumns - 1` will guarantee that the final value F will satisfy
+ // `mRows * mColumns <= F < (mRows + 1) * mColumns
+ if (mRows > (tilesCount + mColumns - 1) / mColumns) {
+ mRows = (tilesCount + mColumns - 1) / mColumns
+ }
+ return previousRows != mRows
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 32b41ec..d72f8e9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -115,34 +115,13 @@
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
// TODO(b/174753536): Move it into the config file.
- // Only do the below hacking when at least one of the below tiles exist
- // --InternetTile
- // --WiFiTile
- // --CellularTIle
- if (possibleTiles.contains("internet") || possibleTiles.contains("wifi")
- || possibleTiles.contains("cell")) {
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- if (!possibleTiles.contains("internet")) {
- possibleTiles.add("internet");
- }
- if (possibleTiles.contains("wifi")) {
- possibleTiles.remove("wifi");
- }
- if (possibleTiles.contains("cell")) {
- possibleTiles.remove("cell");
- }
- } else {
- if (possibleTiles.contains("internet")) {
- possibleTiles.remove("internet");
- }
- if (!possibleTiles.contains("wifi")) {
- possibleTiles.add("wifi");
- }
- if (!possibleTiles.contains("cell")) {
- possibleTiles.add("cell");
- }
- }
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ possibleTiles.remove("cell");
+ possibleTiles.remove("wifi");
+ } else {
+ possibleTiles.remove("internet");
}
+
for (String spec : possibleTiles) {
// Only add current and stock tiles that can be created from QSFactoryImpl.
// Do not include CustomTile. Those will be created by `addPackageTiles`.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index f9d1def..a17aeba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -56,7 +56,6 @@
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
-import com.android.systemui.Prefs;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
@@ -67,7 +66,6 @@
import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSEvent;
import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.logging.QSLogger;
import java.io.FileDescriptor;
@@ -300,11 +298,6 @@
getInstanceId());
mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
mHandler.sendEmptyMessage(H.LONG_CLICK);
-
- Prefs.putInt(
- mContext,
- Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
- QuickStatusBarHeader.MAX_TOOLTIP_SHOWN_COUNT);
}
public LogMaker populate(LogMaker logMaker) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index bd46ffe..4300d37 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -300,7 +300,7 @@
.setColorized(true)
.setColor(getResources().getColor(R.color.GM2_red_700))
.setOngoing(true)
- .setShowForegroundImmediately(true)
+ .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE)
.setContentIntent(
PendingIntent.getService(this, REQUEST_CODE, stopIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index fea521f..bdb3926 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -241,15 +241,7 @@
public void run() {
final float valFloat;
final boolean inVrMode = mIsVrModeEnabled;
- if (inVrMode) {
- valFloat = Settings.System.getFloatForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT, mDefaultBacklightForVr,
- UserHandle.USER_CURRENT);
- } else {
- valFloat = Settings.System.getFloatForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBacklight,
- UserHandle.USER_CURRENT);
- }
+ valFloat = mDisplayManager.getBrightness(mDisplayId);
// Value is passed as intbits, since this is what the message takes.
final int valueAsIntBits = Float.floatToIntBits(valFloat);
mHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits,
@@ -364,14 +356,12 @@
metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR;
minBacklight = mMinimumBacklightForVr;
maxBacklight = mMaximumBacklightForVr;
- settingToChange = Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT;
} else {
metric = mAutomatic
? MetricsEvent.ACTION_BRIGHTNESS_AUTO
: MetricsEvent.ACTION_BRIGHTNESS;
minBacklight = PowerManager.BRIGHTNESS_MIN;
maxBacklight = PowerManager.BRIGHTNESS_MAX;
- settingToChange = Settings.System.SCREEN_BRIGHTNESS_FLOAT;
}
final float valFloat = MathUtils.min(convertGammaToLinearFloat(value,
minBacklight, maxBacklight),
@@ -386,8 +376,7 @@
if (!tracking) {
AsyncTask.execute(new Runnable() {
public void run() {
- Settings.System.putFloatForUser(mContext.getContentResolver(),
- settingToChange, valFloat, UserHandle.USER_CURRENT);
+ mDisplayManager.setBrightness(mDisplayId, valFloat);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index 0b40e22..5d964a4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -27,7 +27,10 @@
import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
@@ -42,9 +45,7 @@
*
* @see BrightnessMirrorController
*/
-public class BrightnessSlider
- extends ViewController<View>
- implements ToggleSlider {
+public class BrightnessSlider extends ViewController<View> implements ToggleSlider {
private Listener mListener;
private ToggleSlider mMirror;
@@ -52,15 +53,34 @@
private BrightnessMirrorController mMirrorController;
private boolean mTracking;
private final boolean mUseMirror;
+ private final FalsingManager mFalsingManager;
+
+ private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ mFalsingManager.isFalseTouch(Classifier.BRIGHTNESS_SLIDER);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return false;
+ }
+ };
BrightnessSlider(
View rootView,
BrightnessSliderView brightnessSliderView,
- boolean useMirror
- ) {
+ boolean useMirror,
+ FalsingManager falsingManager) {
super(rootView);
mBrightnessSliderView = brightnessSliderView;
mUseMirror = useMirror;
+ mFalsingManager = falsingManager;
}
/**
@@ -78,6 +98,7 @@
protected void onViewAttached() {
mBrightnessSliderView.setOnSeekBarChangeListener(mSeekListener);
mBrightnessSliderView.setOnCheckedChangeListener(mCheckListener);
+ mBrightnessSliderView.setOnInterceptListener(mOnInterceptListener);
}
@Override
@@ -85,6 +106,7 @@
mBrightnessSliderView.setOnSeekBarChangeListener(null);
mBrightnessSliderView.setOnCheckedChangeListener(null);
mBrightnessSliderView.setOnDispatchTouchEventListener(null);
+ mBrightnessSliderView.setOnInterceptListener(null);
}
@Override
@@ -247,10 +269,12 @@
public static class Factory {
BrightnessControllerSettings mSettings;
+ private final FalsingManager mFalsingManager;
@Inject
- public Factory(BrightnessControllerSettings settings) {
+ public Factory(BrightnessControllerSettings settings, FalsingManager falsingManager) {
mSettings = settings;
+ mFalsingManager = falsingManager;
}
/**
@@ -270,7 +294,7 @@
private BrightnessSlider fromTree(ViewGroup root, boolean useMirror) {
BrightnessSliderView v = root.requireViewById(R.id.brightness_slider);
- return new BrightnessSlider(root, v, useMirror);
+ return new BrightnessSlider(root, v, useMirror, mFalsingManager);
}
/** Get the layout to inflate based on what slider to use */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index cbf4e88..5b71c62 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -31,6 +31,7 @@
import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
/**
@@ -54,6 +55,7 @@
private TextView mLabel;
private final CharSequence mText;
private DispatchTouchEventListener mListener;
+ private Gefingerpoken mOnInterceptListener;
public BrightnessSliderView(Context context) {
this(context, null);
@@ -105,6 +107,15 @@
return super.dispatchTouchEvent(ev);
}
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ // We prevent disallowing on this view, but bubble it up to our parents.
+ // We need interception to handle falsing.
+ if (mParent != null) {
+ mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+ }
+
/**
* Attaches a listener to the {@link ToggleSeekBar} in the view so changes can be observed
* @param seekListener use {@code null} to remove listener
@@ -195,6 +206,18 @@
return mSlider.getProgress();
}
+ public void setOnInterceptListener(Gefingerpoken onInterceptListener) {
+ mOnInterceptListener = onInterceptListener;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mOnInterceptListener != null) {
+ return mOnInterceptListener.onInterceptTouchEvent(ev);
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
/**
* Interface to attach a listener for {@link View#dispatchTouchEvent}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 708bdfe..90c3dfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -87,10 +87,6 @@
return mFlagReader.isEnabled(R.bool.flag_wallet);
}
- public boolean isNavigationBarOverlayEnabled() {
- return mFlagReader.isEnabled(R.bool.flag_navigation_bar_overlay);
- }
-
public boolean isPMLiteEnabled() {
return mFlagReader.isEnabled(R.bool.flag_pm_lite);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index e5a960e..412d342 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -33,6 +33,7 @@
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Parcelable;
@@ -83,8 +84,15 @@
public static final int STATE_DOT = 1;
public static final int STATE_HIDDEN = 2;
- /** Maximum allowed width or height for an icon drawable */
- private static final int MAX_IMAGE_SIZE = 500;
+ /**
+ * Maximum allowed byte count for an icon bitmap
+ * @see android.graphics.RecordingCanvas.MAX_BITMAP_SIZE
+ */
+ private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
+ /**
+ * Maximum allowed width or height for an icon drawable, if we can't get byte count
+ */
+ private static final int MAX_IMAGE_SIZE = 5000;
private static final String TAG = "StatusBarIconView";
private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
@@ -382,9 +390,18 @@
return false;
}
- if (drawable.getIntrinsicWidth() > MAX_IMAGE_SIZE
+ if (drawable instanceof BitmapDrawable && ((BitmapDrawable) drawable).getBitmap() != null) {
+ // If it's a bitmap we can check the size directly
+ int byteCount = ((BitmapDrawable) drawable).getBitmap().getByteCount();
+ if (byteCount > MAX_BITMAP_SIZE) {
+ Log.w(TAG, "Drawable is too large (" + byteCount + " bytes) " + mIcon);
+ return false;
+ }
+ } else if (drawable.getIntrinsicWidth() > MAX_IMAGE_SIZE
|| drawable.getIntrinsicHeight() > MAX_IMAGE_SIZE) {
- Log.w(TAG, "Drawable is too large " + mIcon);
+ // Otherwise, check dimensions
+ Log.w(TAG, "Drawable is too large (" + drawable.getIntrinsicWidth() + "x"
+ + drawable.getIntrinsicHeight() + ") " + mIcon);
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 138c811..ab17ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -59,6 +59,7 @@
private View mMobileRoamingSpace;
private int mVisibleState = -1;
private DualToneHandler mDualToneHandler;
+ private boolean mForceHidden;
public static StatusBarMobileView fromContext(Context context, String slot) {
LayoutInflater inflater = LayoutInflater.from(context);
@@ -150,7 +151,7 @@
private void initViewState() {
setContentDescription(mState.contentDescription);
- if (!mState.visible) {
+ if (!mState.visible || mForceHidden) {
mMobileGroup.setVisibility(View.GONE);
} else {
mMobileGroup.setVisibility(View.VISIBLE);
@@ -176,8 +177,9 @@
boolean needsLayout = false;
setContentDescription(state.contentDescription);
- if (mState.visible != state.visible) {
- mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
+ int newVisibility = state.visible && !mForceHidden ? View.VISIBLE : View.GONE;
+ if (newVisibility != mMobileGroup.getVisibility()) {
+ mMobileGroup.setVisibility(newVisibility);
needsLayout = true;
}
if (mState.strengthId != state.strengthId) {
@@ -252,7 +254,7 @@
@Override
public boolean isIconVisible() {
- return mState.visible;
+ return mState.visible && !mForceHidden;
}
@Override
@@ -279,6 +281,23 @@
}
}
+ /**
+ * Forces the state to be hidden (views will be GONE) and if necessary updates the layout.
+ *
+ * Makes sure that the {@link StatusBarIconController} cannot make it visible while this flag
+ * is enabled.
+ * @param forceHidden {@code true} if the icon should be GONE in its view regardless of its
+ * state.
+ * {@code false} if the icon should show as determined by its controller.
+ */
+ public void forceHidden(boolean forceHidden) {
+ if (mForceHidden != forceHidden) {
+ mForceHidden = forceHidden;
+ updateState(mState);
+ requestLayout();
+ }
+ }
+
@Override
public int getVisibleState() {
return mVisibleState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
index 6f80317..05af08e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
@@ -25,10 +25,8 @@
import android.graphics.PointF
import android.util.AttributeSet
import android.view.View
-import kotlin.math.max
-private const val RIPPLE_ANIMATION_DURATION: Long = 1500
-private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
+private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
/**
* Expanding ripple effect that shows when charging begins.
@@ -39,17 +37,18 @@
private val defaultColor: Int = 0xffffffff.toInt()
private val ripplePaint = Paint()
+ var radius: Float = 0.0f
+ set(value) { rippleShader.radius = value }
+ var origin: PointF = PointF()
+ set(value) { rippleShader.origin = value }
+ var duration: Long = 1500
+
init {
rippleShader.color = defaultColor
rippleShader.progress = 0f
rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
ripplePaint.shader = rippleShader
- }
-
- override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
- rippleShader.origin = PointF(measuredWidth / 2f, measuredHeight.toFloat())
- rippleShader.radius = max(measuredWidth, measuredHeight).toFloat()
- super.onLayout(changed, left, top, right, bottom)
+ visibility = View.GONE
}
fun startRipple() {
@@ -57,7 +56,7 @@
return // Ignore if ripple effect is already playing
}
val animator = ValueAnimator.ofFloat(0f, 1f)
- animator.duration = RIPPLE_ANIMATION_DURATION
+ animator.duration = duration
animator.addUpdateListener { animator ->
val now = animator.currentPlayTime
val phase = now / 30000f
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
index 5547c1e..d400205 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
@@ -53,7 +53,7 @@
float s = 0.0;
for (float i = 0; i < 4; i += 1) {
float l = i * 0.25;
- float h = l + 0.025;
+ float h = l + 0.005;
float o = abs(sin(0.1 * PI * (t + i)));
s += threshold(n + o, l, h);
}
@@ -97,7 +97,7 @@
float fadeRipple = min(fadeIn, 1.-fadeOutRipple);
float rippleAlpha = softRing(p, in_origin, radius, 0.5)
* fadeRipple * in_color.a;
- vec4 ripple = in_color * max(circle, rippleAlpha) * 0.4;
+ vec4 ripple = in_color * max(circle, rippleAlpha) * 0.3;
return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
}"""
private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index b567ad4..2900462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.content.res.Configuration
+import android.graphics.PointF
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroupOverlay
@@ -31,6 +32,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import java.io.PrintWriter
+import java.lang.Integer.max
import javax.inject.Inject
/***
@@ -46,7 +48,7 @@
private val context: Context,
private val keyguardStateController: KeyguardStateController
) {
- private var pluggedIn: Boolean? = null
+ private var charging: Boolean? = null
private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled
@VisibleForTesting
var rippleView: ChargingRippleView = ChargingRippleView(context, attrs = null)
@@ -55,16 +57,18 @@
val batteryStateChangeCallback = object : BatteryController.BatteryStateChangeCallback {
override fun onBatteryLevelChanged(
level: Int,
- nowPluggedIn: Boolean,
- charging: Boolean
+ pluggedIn: Boolean,
+ nowCharging: Boolean
) {
- if (!rippleEnabled) {
+ // Suppresses the ripple when it's disabled, or when the state change comes
+ // from wireless charging.
+ if (!rippleEnabled || batteryController.isWirelessCharging) {
return
}
- val wasPluggedIn = pluggedIn
- pluggedIn = nowPluggedIn
+ val wasCharging = charging
+ charging = nowCharging
// Only triggers when the keyguard is active and the device is just plugged in.
- if (wasPluggedIn == false && nowPluggedIn && keyguardStateController.isShowing) {
+ if (wasCharging == false && nowCharging && keyguardStateController.isShowing) {
rippleView.startRipple()
}
}
@@ -113,10 +117,13 @@
val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels
if (width != rippleView.width || height != rippleView.height) {
- rippleView.measure(
- View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))
- rippleView.layout(0, 0, width, height)
+ rippleView.apply {
+ measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))
+ layout(0, 0, width, height)
+ origin = PointF(width / 2f, height.toFloat())
+ radius = max(width, height).toFloat()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 85a1aed..23d13d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
import android.animation.Animator;
@@ -34,6 +36,8 @@
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.View;
import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.ScreenDecorationsUtils;
@@ -59,6 +63,14 @@
public static final long ANIMATION_DELAY_ICON_FADE_IN = ANIMATION_DURATION -
CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY
- 16;
+ private static final int ANIMATION_DURATION_NAV_FADE_IN = 266;
+ private static final int ANIMATION_DURATION_NAV_FADE_OUT = 133;
+ private static final long ANIMATION_DELAY_NAV_FADE_IN =
+ ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN;
+ private static final Interpolator NAV_FADE_IN_INTERPOLATOR =
+ new PathInterpolator(0f, 0f, 0f, 1f);
+ private static final Interpolator NAV_FADE_OUT_INTERPOLATOR =
+ new PathInterpolator(0.2f, 0f, 1f, 1f);
private static final long LAUNCH_TIMEOUT = 500;
private final NotificationPanelViewController mNotificationPanel;
private final NotificationListContainer mNotificationContainer;
@@ -208,6 +220,8 @@
int notificationHeight = Math.max(mSourceNotification.getActualHeight()
- mSourceNotification.getClipBottomAmount(), 0);
int notificationWidth = mSourceNotification.getWidth();
+ final RemoteAnimationTarget navigationBarTarget =
+ getNavBarRemoteAnimationTarget(remoteAnimationNonAppTargets);
anim.setDuration(ANIMATION_DURATION);
anim.setInterpolator(Interpolators.LINEAR);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -234,6 +248,7 @@
applyParamsToWindow(primary);
applyParamsToNotification(mParams);
applyParamsToNotificationShade(mParams);
+ applyNavigationBarParamsToWindow(navigationBarTarget);
}
});
anim.addListener(new AnimatorListenerAdapter() {
@@ -286,6 +301,18 @@
return primary;
}
+ private RemoteAnimationTarget getNavBarRemoteAnimationTarget(
+ RemoteAnimationTarget[] remoteAnimationTargets) {
+ RemoteAnimationTarget navBar = null;
+ for (RemoteAnimationTarget target : remoteAnimationTargets) {
+ if (target.windowType == TYPE_NAVIGATION_BAR) {
+ navBar = target;
+ break;
+ }
+ }
+ return navBar;
+ }
+
private void setExpandAnimationRunning(boolean running) {
mNotificationPanel.setLaunchingNotification(running);
mSourceNotification.setExpandAnimationRunning(running);
@@ -326,6 +353,34 @@
mSyncRtTransactionApplier.scheduleApply(params);
}
+ private void applyNavigationBarParamsToWindow(RemoteAnimationTarget navBarTarget) {
+ if (navBarTarget == null) {
+ return;
+ }
+
+ // calculate navigation bar fade-out progress
+ final float fadeOutProgress = mParams.getProgress(0,
+ ANIMATION_DURATION_NAV_FADE_OUT);
+
+ // calculate navigation bar fade-in progress
+ final float fadeInProgress = mParams.getProgress(ANIMATION_DELAY_NAV_FADE_IN,
+ ANIMATION_DURATION_NAV_FADE_OUT);
+
+ final SurfaceParams.Builder builder = new SurfaceParams.Builder(navBarTarget.leash);
+ if (fadeInProgress > 0) {
+ Matrix m = new Matrix();
+ m.postTranslate(0, (float) (mParams.top - navBarTarget.position.y));
+ mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
+ builder.withMatrix(m)
+ .withWindowCrop(mWindowCrop)
+ .withVisibility(true);
+ builder.withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress));
+ } else {
+ builder.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress));
+ }
+ mSyncRtTransactionApplier.scheduleApply(builder.build());
+ }
+
@Override
public void onAnimationCancelled() throws RemoteException {
mMainExecutor.execute(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 609ca97c..3728388 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -25,7 +25,6 @@
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import com.android.systemui.R;
@@ -102,28 +101,6 @@
}
};
- /**
- * Get the relative start padding of a view relative to this view. This recursively walks up the
- * hierarchy and does the corresponding measuring.
- *
- * @param view the view to get the padding for. The requested view has to be a child of this
- * notification.
- * @return the start padding
- */
- public int getRelativeStartPadding(View view) {
- boolean isRtl = isLayoutRtl();
- int startPadding = 0;
- while (view.getParent() instanceof ViewGroup) {
- View parent = (View) view.getParent();
- startPadding += isRtl ? parent.getWidth() - view.getRight() : view.getLeft();
- view = parent;
- if (view == this) {
- return startPadding;
- }
- }
- return startPadding;
- }
-
protected Path getClipPath(boolean ignoreTranslation) {
int left;
int top;
@@ -132,17 +109,15 @@
int height;
float topRoundness = mAlwaysRoundBothCorners
? mOutlineRadius : getCurrentBackgroundRadiusTop();
-
if (!mCustomOutline) {
- // Extend left/right clip bounds beyond the notification by the
- // 1) space between the notification and edge of screen
- // 2) corner radius (so we do not see any rounding as the notification goes off screen)
- left = (int) (-getRelativeStartPadding(this) - mOutlineRadius);
- right = (int) (((View) getParent()).getWidth() + mOutlineRadius);
-
+ int translation = mShouldTranslateContents && !ignoreTranslation
+ ? (int) getTranslation() : 0;
+ int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
+ left = Math.max(translation, 0) - halfExtraWidth;
+ top = mClipTopAmount + mBackgroundTop;
+ right = getWidth() + halfExtraWidth + Math.min(translation, 0);
// If the top is rounded we want the bottom to be at most at the top roundness, in order
// to avoid the shadow changing when scrolling up.
- top = mClipTopAmount + mBackgroundTop;
bottom = Math.max(mMinimumHeightForClipping,
Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness)));
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 1086d67..73e0804 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -69,8 +69,6 @@
private float mContentTranslation;
protected boolean mLastInSection;
protected boolean mFirstInSection;
- private float mOutlineRadius;
- private float mParentWidth;
public ExpandableView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -81,7 +79,6 @@
private void initDimens() {
mContentShift = getResources().getDimensionPixelSize(
R.dimen.shelf_transform_content_shift);
- mOutlineRadius = getResources().getDimensionPixelSize(R.dimen.notification_corner_radius);
}
@Override
@@ -153,9 +150,6 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- if (getParent() != null) {
- mParentWidth = ((View) getParent()).getWidth();
- }
updateClipping();
}
@@ -442,15 +436,11 @@
protected void updateClipping() {
if (mClipToActualHeight && shouldClipToActualHeight()) {
- final int top = getClipTopAmount();
- final int bottom = Math.max(Math.max(getActualHeight() + getExtraBottomPadding()
+ int top = getClipTopAmount();
+ int bottom = Math.max(Math.max(getActualHeight() + getExtraBottomPadding()
- mClipBottomAmount, top), mMinimumHeightForClipping);
- // Extend left/right clip bounds beyond the notification by the
- // 1) space between the notification and edge of screen
- // 2) corner radius (so we do not see any rounding as the notification goes off screen)
- final int left = (int) (-getRelativeStartPadding(this) - mOutlineRadius);
- final int right = (int) (mParentWidth + mOutlineRadius);
- mClipRect.set(left, top, right, bottom);
+ int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
+ mClipRect.set(-halfExtraWidth, top, getWidth() + halfExtraWidth, bottom);
setClipBounds(mClipRect);
} else {
setClipBounds(null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 3ec8c23..4ed5056 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -93,6 +93,13 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ mDismissButton.setBackground(
+ getResources().getDrawable(R.drawable.notif_footer_btn_background));
+ mDismissButton.setTextColor(getResources().getColor(R.color.notif_pill_text));
+ mManageButton.setBackground(
+ getResources().getDrawable(R.drawable.notif_footer_btn_background));
+ mManageButton.setTextColor(getResources().getColor(R.color.notif_pill_text));
+ mManageButton = findViewById(R.id.manage_text);
mDismissButton.setText(R.string.clear_all_notifications_text);
mDismissButton.setContentDescription(
mContext.getString(R.string.accessibility_clear_all));
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 55a27b2..c3ccba4 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
@@ -1291,9 +1291,14 @@
}
}
}
-
- if (existing != null && entry.getSbn().getNotification().isColorized()) {
- existing.overrideBackgroundTintColor(entry.getSbn().getNotification().color);
+ if (existing != null) {
+ if (entry.getSbn().getNotification().isColorized()) {
+ existing.setBackgroundTintColor(
+ entry.getSbn().getNotification().color, true);
+ } else {
+ existing.setBackgroundTintColor(
+ entry.getRow().getCurrentBackgroundTint(), false);
+ }
}
return existing;
}
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 c83b60d..d581c4b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -184,7 +184,6 @@
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CircleReveal;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
@@ -2469,39 +2468,19 @@
protected void showChargingAnimation(int batteryLevel, int transmittingBatteryLevel,
long animationDelay) {
- if (mDozing || mKeyguardManager.isKeyguardLocked()) {
- // on ambient or lockscreen, hide notification panel
- WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
- transmittingBatteryLevel, batteryLevel,
- new WirelessChargingAnimation.Callback() {
- @Override
- public void onAnimationStarting() {
- mNotificationShadeWindowController.setRequestTopUi(true, TAG);
- CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1);
- }
+ WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
+ transmittingBatteryLevel, batteryLevel,
+ new WirelessChargingAnimation.Callback() {
+ @Override
+ public void onAnimationStarting() {
+ mNotificationShadeWindowController.setRequestTopUi(true, TAG);
+ }
- @Override
- public void onAnimationEnded() {
- CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView());
- mNotificationShadeWindowController.setRequestTopUi(false, TAG);
- }
- }, mDozing).show(animationDelay);
- } else {
- // workspace
- WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
- transmittingBatteryLevel, batteryLevel,
- new WirelessChargingAnimation.Callback() {
- @Override
- public void onAnimationStarting() {
- mNotificationShadeWindowController.setRequestTopUi(true, TAG);
- }
-
- @Override
- public void onAnimationEnded() {
- mNotificationShadeWindowController.setRequestTopUi(false, TAG);
- }
- }, false).show(animationDelay);
- }
+ @Override
+ public void onAnimationEnded() {
+ mNotificationShadeWindowController.setRequestTopUi(false, TAG);
+ }
+ }, false).show(animationDelay);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index c6f7983..779ba1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -33,6 +33,8 @@
import android.view.WindowInsets;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+
import com.android.systemui.util.leak.RotationUtils;
/**
@@ -91,6 +93,7 @@
* @param roundedCornerContentPadding
* @return
*/
+ @NonNull
public static Pair<Integer, Integer> paddingNeededForCutoutAndRoundedCorner(
DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding,
int roundedCornerContentPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index f65f97a..64a497d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -262,6 +262,25 @@
}
/**
+ * Returns the view corresponding to a particular slot.
+ *
+ * Use it solely to manipulate how it is presented.
+ * @param slot name of the slot to find. Names are defined in
+ * {@link com.android.internal.R.config_statusBarIcons}
+ * @return a view for the slot if this container has it, else {@code null}
+ */
+ public View getViewForSlot(String slot) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof StatusIconDisplayable
+ && ((StatusIconDisplayable) child).getSlot().equals(slot)) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
* Layout is happening from end -> start
*/
private void calculateIconTranslations() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 4615877..fd37d89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -321,6 +321,11 @@
}
@Override
+ public int getDoneText() {
+ return R.string.quick_settings_close_user_panel;
+ }
+
+ @Override
public boolean onDoneButtonClicked() {
if (mNotificationPanelViewController != null) {
mNotificationPanelViewController.animateCloseQs(true /* animateAway */);
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 f72d2ae..5a78ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -32,7 +32,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
import android.content.res.ColorStateList;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
@@ -74,6 +73,7 @@
import androidx.annotation.NonNull;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.statusbar.IStatusBarService;
@@ -130,6 +130,9 @@
private int mRevealCy;
private int mRevealR;
+ private boolean mColorized;
+ private int mTint;
+
private boolean mResetting;
private NotificationViewWrapper mWrapper;
private Consumer<Boolean> mOnVisibilityChangedListener;
@@ -143,35 +146,63 @@
mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
mStatusBarManagerService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{
+ com.android.internal.R.attr.colorAccent,
+ com.android.internal.R.attr.colorBackgroundFloating,
+ });
+ mTint = ta.getColor(0, 0);
+ ta.recycle();
}
/**
* The remote view needs to adapt to colorized notifications when set
+ * It overrides the background of itself as well as all of its childern
* @param color colorized notification color
*/
- public void overrideBackgroundTintColor(int color) {
- mEditText.setBackgroundTintColor(color);
- final boolean dark = !ContrastColorUtil.isColorLight(color);
- int[][] states = new int[][] {
- new int[] {android.R.attr.state_enabled},
- new int[] {},
+ public void setBackgroundTintColor(int color, boolean colorized) {
+ if (colorized == mColorized && color == mTint) return;
+ mColorized = colorized;
+ mTint = color;
+ final int[][] states = new int[][]{
+ new int[]{com.android.internal.R.attr.state_enabled},
+ new int[]{},
};
+ final int[] colors;
+ if (colorized) {
+ final boolean dark = !ContrastColorUtil.isColorLight(color);
+ final int finalColor = dark
+ ? Color.WHITE
+ : Color.BLACK;
+ colors = new int[]{
+ finalColor,
+ finalColor & 0x4DFFFFFF // %30 opacity
+ };
+ mEditText.setUniformBackgroundTintColor(color);
+ mEditText.setUniformForegroundColor(finalColor);
- final int finalColor = dark
- ? Color.WHITE
- : Color.BLACK;
-
- int[] colors = new int[] {
- finalColor,
- finalColor & 0x4DFFFFFF // %30 opacity
- };
-
- final ColorStateList tint = new ColorStateList(states, colors);
+ } else {
+ mEditText.setTextColor(mContext.getColor(R.color.remote_input_text));
+ mEditText.setHintTextColor(mContext.getColorStateList(R.color.remote_input_hint));
+ TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{
+ com.android.internal.R.attr.colorAccent,
+ com.android.internal.R.attr.colorBackgroundFloating,
+ });
+ int colorAccent = ta.getColor(0, 0);
+ int colorBackgroundFloating = ta.getColor(1, 0);
+ ta.recycle();
+ mEditText.setTextBackgroundColors(colorAccent, colorBackgroundFloating);
+ colors = new int[]{
+ colorAccent,
+ colorBackgroundFloating & 0x4DFFFFFF // %30 opacity
+ };
+ }
+ mEditText.setBackgroundColor(color);
+ final ColorStateList tint = new ColorStateList(states, colors);
mSendButton.setImageTintList(tint);
mProgressBar.setProgressTintList(tint);
mProgressBar.setIndeterminateTintList(tint);
mProgressBar.setSecondaryProgressTintList(tint);
- mEditText.setForegroundColor(finalColor);
+ setBackgroundColor(color);
}
@Override
@@ -326,7 +357,7 @@
reveal.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- setVisibility(INVISIBLE);
+ setVisibility(GONE);
if (mWrapper != null) {
mWrapper.setRemoteInputVisible(false);
}
@@ -334,7 +365,7 @@
});
reveal.start();
} else {
- setVisibility(INVISIBLE);
+ setVisibility(GONE);
if (mWrapper != null) {
mWrapper.setRemoteInputVisible(false);
}
@@ -398,12 +429,6 @@
}
}
- @Override
- public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mEditText.updateCornerRadius(heightMeasureSpec / 2);
- }
-
/** Populates the text field of the remote input with the given content. */
public void setEditTextContent(@Nullable CharSequence editTextContent) {
mEditText.setText(editTextContent);
@@ -700,40 +725,15 @@
boolean mShowImeOnInputConnection;
private LightBarController mLightBarController;
private InputMethodManager mInputMethodManager;
- private int mColor = Notification.COLOR_DEFAULT;
UserHandle mUser;
- private int mStokeWidth;
public RemoteEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mLightBarController = Dependency.get(LightBarController.class);
- mTextBackground = createBackground(context, attrs);
+ mTextBackground = (GradientDrawable)
+ context.getDrawable(R.drawable.remote_input_view_text_bg).mutate();
mBackgroundColor = new ColorDrawable();
mBackground = new LayerDrawable(new Drawable[] {mBackgroundColor, mTextBackground});
- float density = context.getResources().getDisplayMetrics().density;
- mStokeWidth = (int) (2 * density);
- setDefaultColors();
- }
-
- private void setDefaultColors() {
- Resources.Theme theme = getContext().getTheme();
- TypedArray ta = theme.obtainStyledAttributes(
- new int[]{android.R.attr.colorAccent,
- com.android.internal.R.attr.colorBackgroundFloating});
- mTextBackground.setStroke(mStokeWidth,
- ta.getColor(0, Notification.COLOR_DEFAULT));
- mColor = ta.getColor(1, Notification.COLOR_DEFAULT);
- mTextBackground.setColor(mColor);
- }
-
- private GradientDrawable createBackground(Context context, AttributeSet attrs) {
- float density = context.getResources().getDisplayMetrics().density;
- int padding = (int) (12 * density);
- GradientDrawable d = new GradientDrawable();
- d.setShape(GradientDrawable.RECTANGLE);
- d.setPadding(padding, padding, padding, padding);
- d.setCornerRadius(padding);
- return d;
}
void setSupportedMimeTypes(@Nullable Collection<String> mimeTypes) {
@@ -796,16 +796,17 @@
}
}
- protected void setBackgroundTintColor(int color) {
+ protected void setUniformBackgroundTintColor(int color) {
mBackgroundColor.setColor(color);
mTextBackground.setColor(color);
}
- protected void setForegroundColor(int color) {
- mTextBackground.setStroke(mStokeWidth, color);
+ protected void setUniformForegroundColor(int color) {
+ int stroke = getContext().getResources()
+ .getDimensionPixelSize(R.dimen.remote_input_view_text_stroke);
+ mTextBackground.setStroke(stroke, color);
setTextColor(color);
- // %60
- setHintTextColor(color & 0x99FFFFFF);
+ setHintTextColor(ColorUtils.setAlphaComponent(color, 0x99));
setTextCursorDrawable(null);
}
@@ -896,10 +897,6 @@
setSelection(getText().length());
}
- void updateCornerRadius(float radius) {
- mTextBackground.setCornerRadius(radius);
- }
-
void setInnerFocusable(boolean focusable) {
setFocusableInTouchMode(focusable);
setFocusable(focusable);
@@ -924,16 +921,28 @@
if (clip.getItemCount() > 1
|| description.getMimeTypeCount() < 1
|| remainingItems != null) {
- // TODO(b/172363500): Update to loop over all the items
+ // Direct-reply in notifications currently only supports only one uri item
+ // at a time and requires the MIME type to be set.
+ Log.w(TAG, "Invalid payload: " + payload);
return payload;
}
Uri contentUri = clip.getItemAt(0).getUri();
String mimeType = description.getMimeType(0);
Intent dataIntent =
mRemoteInputView.prepareRemoteInputFromData(mimeType, contentUri);
+ // We can release the uri permissions granted to us as soon as we've created the
+ // grant for the target app in the call above.
+ payload.releasePermissions();
mRemoteInputView.sendRemoteInput(dataIntent);
}
return remainingItems;
}
+
+ protected void setTextBackgroundColors(int strokeColor, int textBackground) {
+ mTextBackground.setColor(textBackground);
+ int stroke = getContext().getResources()
+ .getDimensionPixelSize(R.dimen.remote_input_view_text_stroke);
+ mTextBackground.setStroke(stroke, strokeColor);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java
index 3bc2632..95216c5 100644
--- a/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyCallback.java
@@ -28,6 +28,12 @@
import javax.inject.Inject;
+/**
+ * Class for use by {@link TelephonyListenerManager} to centralize TelephonyManager Callbacks.
+ *
+ * There are more callback interfaces defined in {@link android.telephony.TelephonyCallback} that
+ * are not currently covered. Add them here if they ever become necessary.
+ */
class TelephonyCallback extends android.telephony.TelephonyCallback
implements ActiveDataSubscriptionIdListener, CallStateListener, ServiceStateListener {
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java
index 4e1acca..3111930 100644
--- a/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java
+++ b/packages/SystemUI/src/com/android/systemui/telephony/TelephonyListenerManager.java
@@ -47,7 +47,9 @@
private boolean mListening = false;
@Inject
- public TelephonyListenerManager(TelephonyManager telephonyManager, @Main Executor executor,
+ public TelephonyListenerManager(
+ TelephonyManager telephonyManager,
+ @Main Executor executor,
TelephonyCallback telephonyCallback) {
mTelephonyManager = telephonyManager;
mExecutor = executor;
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 709ccd4..e403cfa 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -44,6 +44,7 @@
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -105,6 +106,12 @@
@WMSingleton
@Provides
+ static DisplayLayout provideDisplayLayout() {
+ return new DisplayLayout();
+ }
+
+ @WMSingleton
+ @Provides
static DragAndDropController provideDragAndDropController(Context context,
DisplayController displayController) {
return new DragAndDropController(context, displayController);
@@ -233,11 +240,13 @@
@Provides
static Optional<OneHandedController> provideOneHandedController(Context context,
WindowManager windowManager, DisplayController displayController,
- TaskStackListenerImpl taskStackListener, UiEventLogger uiEventLogger,
+ DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
+ UiEventLogger uiEventLogger,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return Optional.ofNullable(OneHandedController.create(context, windowManager,
- displayController, taskStackListener, uiEventLogger, mainExecutor, mainHandler));
+ displayController, displayLayout, taskStackListener, uiEventLogger, mainExecutor,
+ mainHandler));
}
//
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
index db06199..c912419 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.classifier;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+
import static com.google.common.truth.Truth.assertThat;
import android.testing.AndroidTestingRunner;
@@ -96,13 +98,9 @@
}
@Test
- public void testPass_swipe() {
-
+ public void testPass_BrightnessSliderAlwaysPasses() {
mClassifier.onTouchEvent(appendDownEvent(1, 1));
- assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue();
-
- mClassifier.onTouchEvent(appendMoveEvent(1, mDataProvider.getYdpi() * 3, 3));
- mClassifier.onTouchEvent(appendUpEvent(1, mDataProvider.getYdpi() * 3, 300));
- assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 1).isFalse())
+ .isFalse();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
index 52423bd..60786f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
@@ -16,11 +16,12 @@
package com.android.systemui.classifier;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -72,7 +73,7 @@
public void testPass_uncovered() {
touchDown();
touchUp(10);
- assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
+ assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
}
@Test
@@ -81,7 +82,7 @@
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 2));
touchUp(20);
- assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
+ assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
}
@Test
@@ -90,7 +91,17 @@
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
- assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse(), is(false));
+ assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse()).isFalse();
+ }
+
+ @Test
+ public void testPass_brightnessSlider() {
+ touchDown();
+ mClassifier.onProximityEvent(createSensorEvent(true, 1));
+ mClassifier.onProximityEvent(createSensorEvent(false, 11));
+ touchUp(10);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse())
+ .isFalse();
}
@Test
@@ -99,7 +110,7 @@
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
- assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(true));
+ assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue();
}
@Test
@@ -110,7 +121,7 @@
mClassifier.onProximityEvent(createSensorEvent(true, 96));
mClassifier.onProximityEvent(createSensorEvent(false, 100));
touchUp(100);
- assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(true));
+ assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue();
}
@Test
@@ -120,7 +131,7 @@
mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
when(mDistanceClassifier.isLongSwipe()).thenReturn(mPassedResult);
- assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
+ assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
}
private void touchDown() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
index 068a1e5..32537b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.classifier;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
@@ -277,4 +278,46 @@
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(RIGHT_AFFORDANCE, 0.5, 0).isFalse()).isTrue();
}
+
+ @Test
+ public void testPass_BrightnessSlider() {
+ when(mDataProvider.isVertical()).thenReturn(false);
+
+ when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+ }
+
+ @Test
+ public void testFalse_BrightnessSlider() {
+ when(mDataProvider.isVertical()).thenReturn(true);
+
+ when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
index e004c30..c343c20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.classifier;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+
import static com.google.common.truth.Truth.assertThat;
import android.testing.AndroidTestingRunner;
@@ -82,6 +84,13 @@
assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
}
+ @Test
+ public void testPass_brightnessSliderAlwaysPasses() {
+ appendMoveEvent(0, 0);
+ appendMoveEvent(0, 100);
+ appendMoveEvent(0, 1);
+ assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 1).isFalse()).isFalse();
+ }
@Test
public void testFail_minimumTouchesVertical() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 7090e78..d91625e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -17,11 +17,14 @@
package com.android.systemui.people.widget;
import static android.app.Notification.CATEGORY_MISSED_CALL;
+import static android.app.Notification.EXTRA_PEOPLE_LIST;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
+import static android.content.PermissionChecker.PERMISSION_GRANTED;
+import static android.content.PermissionChecker.PERMISSION_HARD_DENIED;
import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
@@ -55,6 +58,7 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -86,6 +90,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -106,6 +111,8 @@
private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
private static final int WIDGET_ID_WITH_KEY_IN_OPTIONS = 4;
+ private static final int WIDGET_ID_WITH_SAME_URI = 5;
+ private static final int WIDGET_ID_WITH_DIFFERENT_URI = 6;
private static final String SHORTCUT_ID = "101";
private static final String OTHER_SHORTCUT_ID = "102";
private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
@@ -123,10 +130,21 @@
new PeopleSpaceTile
.Builder(SHORTCUT_ID, "username", ICON, new Intent())
.setPackageName(TEST_PACKAGE_A)
- .setUserHandle(new UserHandle(1))
+ .setUserHandle(new UserHandle(0))
.setNotificationKey(NOTIFICATION_KEY + "1")
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
+ .setContactUri(URI)
+ .build();
+ private static final PeopleSpaceTile PERSON_TILE_WITH_SAME_URI =
+ new PeopleSpaceTile
+ // Different shortcut ID
+ .Builder(OTHER_SHORTCUT_ID, "username", ICON, new Intent())
+ // Different package name
+ .setPackageName(TEST_PACKAGE_B)
+ .setUserHandle(new UserHandle(0))
+ // Same contact uri.
+ .setContactUri(URI)
.build();
private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
SHORTCUT_ID).setLongLabel("name").build();
@@ -149,6 +167,8 @@
private LauncherApps mLauncherApps;
@Mock
private NotificationEntryManager mNotificationEntryManager;
+ @Mock
+ private PackageManager mPackageManager;
@Captor
private ArgumentCaptor<NotificationHandler> mListenerCaptor;
@@ -167,7 +187,7 @@
mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager);
mManager = new PeopleSpaceWidgetManager(mContext);
mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager,
- mLauncherApps, mNotificationEntryManager);
+ mLauncherApps, mNotificationEntryManager, mPackageManager, true);
mManager.attach(mListenerService);
mProvider = new PeopleSpaceWidgetProvider();
mProvider.setPeopleSpaceWidgetManager(mManager);
@@ -177,16 +197,10 @@
mNoMan.addListener(serviceListener);
clearStorage();
- setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
-
- Bundle options = new Bundle();
- options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
- when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
- .thenReturn(options);
+ addTileForWidget(PERSON_TILE, WIDGET_ID_WITH_SHORTCUT);
+ addTileForWidget(PERSON_TILE_WITH_SAME_URI, WIDGET_ID_WITH_SAME_URI);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
.thenReturn(new Bundle());
- when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(
- getConversationWithShortcutId(SHORTCUT_ID));
}
@Test
@@ -477,7 +491,7 @@
addSecondWidgetForPersonTile();
PeopleSpaceUtils.removeSharedPreferencesStorageForTile(
- mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT);
+ mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT, EMPTY_STRING);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(createNotification(
SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
@@ -501,7 +515,6 @@
throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(createNotification(
@@ -527,7 +540,6 @@
throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(createNotification(
@@ -548,10 +560,267 @@
}
@Test
+ public void testUpdateMissedCallNotificationWithContentPostedIfMatchingUriTile()
+ throws Exception {
+ int[] widgetIdsArray =
+ {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+ PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+ assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
+ NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI),
+ mBundleArgumentCaptor.capture());
+ Bundle bundleForSameUriTile = requireNonNull(mBundleArgumentCaptor.getValue());
+ PeopleSpaceTile tileWithSameUri = bundleForSameUriTile.getParcelable(OPTIONS_PEOPLE_TILE);
+ assertThat(tileWithSameUri.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tileWithSameUri.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+ any());
+ }
+
+ @Test
+ public void testRemoveMissedCallNotificationWithContentPostedIfMatchingUriTile()
+ throws Exception {
+ int[] widgetIdsArray =
+ {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+ NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn.cloneLight(), 0);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+ assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(null);
+ assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(null);
+ verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(2))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI),
+ mBundleArgumentCaptor.capture());
+ Bundle bundleForSameUriTile = requireNonNull(mBundleArgumentCaptor.getValue());
+ PeopleSpaceTile tileWithSameUri = bundleForSameUriTile.getParcelable(OPTIONS_PEOPLE_TILE);
+ assertThat(tileWithSameUri.getNotificationKey()).isEqualTo(null);
+ assertThat(tileWithSameUri.getNotificationContent()).isEqualTo(null);
+ verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+ any());
+ }
+
+ @Test
+ public void testDoNotRemoveMissedCallIfMatchingUriTileMissingReadContactsPermissionWhenPosted()
+ throws Exception {
+ when(mPackageManager.checkPermission(any(),
+ eq(PERSON_TILE_WITH_SAME_URI.getPackageName()))).thenReturn(
+ PERMISSION_HARD_DENIED);
+ int[] widgetIdsArray =
+ {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+ // We should only try to remove the notification if the Missed Call was added when posted.
+ NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn.cloneLight(), 0);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+ assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(null);
+ assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(null);
+ verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(0))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI), any());
+ verify(mAppWidgetManager, times(0)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+ any());
+ }
+
+ @Test
+ public void testUpdateMissedCallNotificationWithContentPostedIfMatchingUriTileFromSender()
+ throws Exception {
+ int[] widgetIdsArray =
+ {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ Notification notificationWithPersonOnlyInSender =
+ createMessagingStyleNotificationWithoutExtras(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */
+ true).build();
+ StatusBarNotification sbn = new SbnBuilder()
+ .setNotification(notificationWithPersonOnlyInSender)
+ .setPkg(TEST_PACKAGE_A)
+ .setUid(0)
+ .setUser(new UserHandle(0))
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbn)
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+ PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+ assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
+ NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI),
+ mBundleArgumentCaptor.capture());
+ Bundle bundleForSameUriTile = requireNonNull(mBundleArgumentCaptor.getValue());
+ PeopleSpaceTile tileWithSameUri = bundleForSameUriTile.getParcelable(OPTIONS_PEOPLE_TILE);
+ assertThat(tileWithSameUri.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tileWithSameUri.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+ any());
+ }
+
+ @Test
+ public void testDoNotUpdateMissedCallNotificationWithContentPostedIfNoPersonsAttached()
+ throws Exception {
+ int[] widgetIdsArray =
+ {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ // Notification posted without any Person attached.
+ Notification notificationWithoutPersonObject =
+ createMessagingStyleNotificationWithoutExtras(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */
+ true).setStyle(new Notification.MessagingStyle("sender")
+ .addMessage(
+ new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+ "sender"))
+ ).build();
+ StatusBarNotification sbn = new SbnBuilder()
+ .setNotification(notificationWithoutPersonObject)
+ .setPkg(TEST_PACKAGE_A)
+ .setUid(0)
+ .setUser(new UserHandle(0))
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbn)
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+ PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+ assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
+ NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ // Do not update since notification doesn't include a Person reference.
+ verify(mAppWidgetManager, times(0))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI),
+ any());
+ verify(mAppWidgetManager, times(0)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+ any());
+ }
+
+ @Test
+ public void testDoNotUpdateMissedCallNotificationWithContentPostedIfNotMatchingUriTile()
+ throws Exception {
+ clearStorage();
+ addTileForWidget(PERSON_TILE, WIDGET_ID_WITH_SHORTCUT);
+ addTileForWidget(PERSON_TILE_WITH_SAME_URI.toBuilder().setContactUri(
+ Uri.parse("different_uri")).build(), WIDGET_ID_WITH_DIFFERENT_URI);
+ int[] widgetIdsArray =
+ {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_DIFFERENT_URI};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+ PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+ assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
+ NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ // Do not update since missing permission to read contacts.
+ verify(mAppWidgetManager, times(0))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_DIFFERENT_URI),
+ any());
+ verify(mAppWidgetManager, times(0)).updateAppWidget(eq(WIDGET_ID_WITH_DIFFERENT_URI),
+ any());
+ }
+
+ @Test
+ public void testDoNotUpdateMissedCallIfMatchingUriTileMissingReadContactsPermission()
+ throws Exception {
+ when(mPackageManager.checkPermission(any(),
+ eq(PERSON_TILE_WITH_SAME_URI.getPackageName()))).thenReturn(
+ PERMISSION_HARD_DENIED);
+ int[] widgetIdsArray =
+ {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, WIDGET_ID_WITH_SAME_URI};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createNotification(
+ SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue());
+ PeopleSpaceTile tileWithMissedCallOrigin = bundle.getParcelable(OPTIONS_PEOPLE_TILE);
+ assertThat(tileWithMissedCallOrigin.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tileWithMissedCallOrigin.getNotificationContent()).isEqualTo(
+ NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ // Do not update since missing permission to read contacts.
+ verify(mAppWidgetManager, times(0))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SAME_URI),
+ any());
+ verify(mAppWidgetManager, times(0)).updateAppWidget(eq(WIDGET_ID_WITH_SAME_URI),
+ any());
+ }
+
+ @Test
public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
StatusBarNotification sbn = createNotification(
SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -574,7 +843,8 @@
}
@Test
- public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners() {
+ public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners()
+ throws Exception {
addSecondWidgetForPersonTile();
mProvider.onUpdate(mContext, mAppWidgetManager,
new int[]{WIDGET_ID_WITH_SHORTCUT, SECOND_WIDGET_ID_WITH_SHORTCUT});
@@ -746,19 +1016,26 @@
* Adds another widget for {@code PERSON_TILE} with widget ID: {@code
* SECOND_WIDGET_ID_WITH_SHORTCUT}.
*/
- private void addSecondWidgetForPersonTile() {
- Bundle options = new Bundle();
- options.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
- when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
- .thenReturn(options);
+ private void addSecondWidgetForPersonTile() throws Exception {
// Set the same Person associated on another People Tile widget ID.
- setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
- setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+ addTileForWidget(PERSON_TILE, SECOND_WIDGET_ID_WITH_SHORTCUT);
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
SECOND_WIDGET_ID_WITH_SHORTCUT};
when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
}
+ private void addTileForWidget(PeopleSpaceTile tile, int widgetId) throws Exception {
+ setStorageForTile(tile.getId(), tile.getPackageName(), widgetId, tile.getContactUri());
+ Bundle options = new Bundle();
+ options.putParcelable(OPTIONS_PEOPLE_TILE, tile);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(widgetId)))
+ .thenReturn(options);
+ when(mIPeopleManager.getConversation(tile.getPackageName(), 0, tile.getId())).thenReturn(
+ getConversationWithShortcutId(tile.getId()));
+ when(mPackageManager.checkPermission(any(), eq(tile.getPackageName()))).thenReturn(
+ PERMISSION_GRANTED);
+ }
+
/**
* Returns a single conversation associated with {@code shortcutId}.
*/
@@ -772,7 +1049,7 @@
private ConversationChannel getConversationWithShortcutId(String shortcutId,
List<ConversationStatus> statuses) throws Exception {
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
- "name").build();
+ "name").setPerson(PERSON).build();
ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
0L, false, false, statuses);
return convo;
@@ -780,6 +1057,30 @@
private Notification createMessagingStyleNotification(String shortcutId,
boolean isMessagingStyle, boolean isMissedCall) {
+ Bundle extras = new Bundle();
+ ArrayList<Person> person = new ArrayList<Person>();
+ person.add(PERSON);
+ extras.putParcelableArrayList(EXTRA_PEOPLE_LIST, person);
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setContentTitle("TEST_TITLE")
+ .setContentText("TEST_TEXT")
+ .setExtras(extras)
+ .setShortcutId(shortcutId);
+ if (isMessagingStyle) {
+ builder.setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(
+ new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+ PERSON))
+ );
+ }
+ if (isMissedCall) {
+ builder.setCategory(CATEGORY_MISSED_CALL);
+ }
+ return builder.build();
+ }
+
+ private Notification.Builder createMessagingStyleNotificationWithoutExtras(String shortcutId,
+ boolean isMessagingStyle, boolean isMissedCall) {
Notification.Builder builder = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
@@ -794,9 +1095,10 @@
if (isMissedCall) {
builder.setCategory(CATEGORY_MISSED_CALL);
}
- return builder.build();
+ return builder;
}
+
private StatusBarNotification createNotification(String shortcutId,
boolean isMessagingStyle, boolean isMissedCall) {
Notification notification = createMessagingStyleNotification(
@@ -804,6 +1106,8 @@
return new SbnBuilder()
.setNotification(notification)
.setPkg(TEST_PACKAGE_A)
+ .setUid(0)
+ .setUser(new UserHandle(0))
.build();
}
@@ -824,11 +1128,16 @@
String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS),
Context.MODE_PRIVATE);
widgetSp4.edit().clear().commit();
+ SharedPreferences widgetSp5 = mContext.getSharedPreferences(
+ String.valueOf(WIDGET_ID_WITH_SAME_URI),
+ Context.MODE_PRIVATE);
+ widgetSp5.edit().clear().commit();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
sp.edit().clear().commit();
}
- private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
+ private void setStorageForTile(String shortcutId, String packageName, int widgetId,
+ Uri contactUri) {
SharedPreferences widgetSp = mContext.getSharedPreferences(
String.valueOf(widgetId),
Context.MODE_PRIVATE);
@@ -840,11 +1149,17 @@
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sp.edit();
- editor.putString(String.valueOf(widgetId), shortcutId);
+ editor.putString(String.valueOf(widgetId), contactUri.toString());
+
String key = new PeopleTileKey(shortcutId, 0, packageName).toString();
Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
storedWidgetIds.add(String.valueOf(widgetId));
editor.putStringSet(key, storedWidgetIds);
+
+ Set<String> storedWidgetIdsByUri = new HashSet<>(
+ sp.getStringSet(contactUri.toString(), new HashSet<>()));
+ storedWidgetIdsByUri.add(String.valueOf(widgetId));
+ editor.putStringSet(contactUri.toString(), storedWidgetIdsByUri);
editor.apply();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 3595095..3c1b36e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -21,9 +21,9 @@
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
@@ -32,16 +32,12 @@
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.policy.Clock
-import com.android.systemui.statusbar.policy.NextAlarmController
-import com.android.systemui.util.RingerModeTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
-import com.android.systemui.utils.leaks.FakeZenModeController
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -61,26 +57,16 @@
@Mock
private lateinit var view: QuickStatusBarHeader
@Mock
- private lateinit var zenModeController: FakeZenModeController
- @Mock
- private lateinit var nextAlarmController: NextAlarmController
- @Mock
private lateinit var privacyItemController: PrivacyItemController
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private lateinit var ringerModeTracker: RingerModeTracker
@Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var uiEventLogger: UiEventLogger
@Mock
- private lateinit var qsTileHost: QSTileHost
- @Mock
private lateinit var statusBarIconController: StatusBarIconController
@Mock
private lateinit var demoModeController: DemoModeController
@Mock
- private lateinit var userTracker: UserTracker
- @Mock
private lateinit var quickQSPanelController: QuickQSPanelController
@Mock(answer = Answers.RETURNS_SELF)
private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
@@ -105,6 +91,8 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var context: Context
+ private val qsExpansionPathInterpolator = QSExpansionPathInterpolator()
+
private lateinit var controller: QuickStatusBarHeaderController
@Before
@@ -119,21 +107,17 @@
controller = QuickStatusBarHeaderController(
view,
- zenModeController,
- nextAlarmController,
privacyItemController,
- ringerModeTracker,
activityStarter,
uiEventLogger,
- qsTileHost,
statusBarIconController,
demoModeController,
- userTracker,
quickQSPanelController,
qsCarrierGroupControllerBuilder,
privacyLogger,
colorExtractor,
- privacyDialogController
+ privacyDialogController,
+ qsExpansionPathInterpolator
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 022dc84..104b625 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -21,10 +21,13 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.FeatureFlagUtils;
import android.view.LayoutInflater;
import androidx.test.filters.SmallTest;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -39,6 +42,7 @@
private QSCarrier mQSCarrier;
private TestableLooper mTestableLooper;
+ private int mSignalIconId;
@Before
public void setUp() throws Exception {
@@ -46,18 +50,26 @@
LayoutInflater inflater = LayoutInflater.from(mContext);
mTestableLooper.runWithLooper(() ->
mQSCarrier = (QSCarrier) inflater.inflate(R.layout.qs_carrier, null));
+
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ // In this case, the id is an actual drawable id
+ mSignalIconId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
+ } else {
+ // In this case, the id is a level
+ mSignalIconId = SignalDrawable.getEmptyState(5);
+ }
}
@Test
public void testUpdateState_first() {
- CellSignalState c = new CellSignalState(true, 0, "", "", false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c));
}
@Test
public void testUpdateState_same() {
- CellSignalState c = new CellSignalState(true, 0, "", "", false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c));
assertFalse(mQSCarrier.updateState(c));
@@ -65,7 +77,7 @@
@Test
public void testUpdateState_changed() {
- CellSignalState c = new CellSignalState(true, 0, "", "", false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
index 0cc2072..95e9d1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
@@ -25,6 +25,7 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.RestrictedLockUtils
import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.statusbar.policy.BrightnessMirrorController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
@@ -75,6 +76,7 @@
private lateinit var checkedChangeCaptor: ArgumentCaptor<CompoundButton.OnCheckedChangeListener>
@Mock
private lateinit var compoundButton: CompoundButton
+ private var mFalsingManager: FalsingManagerFake = FalsingManagerFake()
private lateinit var mController: BrightnessSlider
@@ -85,7 +87,7 @@
whenever(mirrorController.toggleSlider).thenReturn(mirror)
whenever(motionEvent.copy()).thenReturn(motionEvent)
- mController = BrightnessSlider(rootView, brightnessSliderView, true)
+ mController = BrightnessSlider(rootView, brightnessSliderView, true, mFalsingManager)
mController.init()
mController.setOnChangedListener(listener)
}
@@ -160,7 +162,8 @@
@Test
fun testSettingMirrorWhenNotUseMirrorIsNoOp() {
- val otherController = BrightnessSlider(rootView, brightnessSliderView, false)
+ val otherController = BrightnessSlider(
+ rootView, brightnessSliderView, false, mFalsingManager)
otherController.init()
otherController.setMirrorControllerAndMirror(mirrorController)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index f31639c..7c819f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -125,7 +125,7 @@
@Test
public void testGiantImageNotAllowed() {
- Bitmap largeBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
+ Bitmap largeBitmap = Bitmap.createBitmap(6000, 6000, Bitmap.Config.ARGB_8888);
Icon icon = Icon.createWithBitmap(largeBitmap);
StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
icon, 0, 0, "");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index 3701b91..9ce7241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -93,8 +93,8 @@
captor.value.onBatteryLevelChanged(
unusedBatteryLevel,
- true /* plugged in */,
- false /* charging */)
+ false /* plugged in */,
+ true /* charging */)
verify(rippleView).startRipple()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
index 463b336..ac15903 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
@@ -27,7 +27,6 @@
import com.android.systemui.SysuiTestCase;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,7 +35,7 @@
public class TelephonyCallbackTest extends SysuiTestCase {
private TelephonyCallback mTelephonyCallback = new TelephonyCallback();
-
+
@Test
public void testAddListener_ActiveDataSubscriptionIdListener() {
assertThat(mTelephonyCallback.hasAnyListeners()).isFalse();
diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml
index 443a9bc..f971a09 100644
--- a/packages/VpnDialogs/res/values/strings.xml
+++ b/packages/VpnDialogs/res/values/strings.xml
@@ -28,6 +28,17 @@
]]> appears at the top of your screen when VPN is active.
</string>
+ <!-- TV specific dialog message to warn about the risk of using a VPN application. [CHAR LIMIT=NONE] -->
+ <string name="warning" product="tv">
+ <xliff:g id="app">%s</xliff:g> wants to set up a VPN connection
+ that allows it to monitor network traffic. Only accept if you trust the source.
+ <![CDATA[
+ <br />
+ <br />
+ <img src="vpn_icon" />
+ ]]> appears on your screen when VPN is active.
+ </string>
+
<!-- Dialog title for built-in VPN. [CHAR LIMIT=40] -->
<string name="legacy_title">VPN is connected</string>
<!-- Label for the name of the current VPN session. [CHAR LIMIT=20] -->
diff --git a/services/Android.bp b/services/Android.bp
index f6bb157..c160842 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -9,7 +9,10 @@
filegroup {
name: "services-main-sources",
- srcs: ["java/**/*.java"],
+ srcs: [
+ "java/**/*.java",
+ "java/**/package.html",
+ ],
path: "java",
visibility: ["//visibility:private"],
}
@@ -19,6 +22,7 @@
srcs: [
":services.core-sources",
":services.core-sources-am-wm",
+ "core/java/com/android/server/am/package.html",
":services.accessibility-sources",
":services.appprediction-sources",
":services.appwidget-sources",
@@ -153,7 +157,7 @@
" --hide DeprecationMismatch" +
" --hide HiddenTypedefConstant",
visibility: ["//visibility:private"],
- filter_packages: ["com.android."]
+ filter_packages: ["com.android."],
}
droidstubs {
@@ -168,7 +172,7 @@
last_released: {
api_file: ":android.api.system-server.latest",
removed_api_file: ":removed.api.system-server.latest",
- baseline_file: ":android-incompatibilities.api.system-server.latest"
+ baseline_file: ":android-incompatibilities.api.system-server.latest",
},
api_lint: {
enabled: true,
@@ -178,18 +182,24 @@
},
dists: [
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "android.txt",
- tag: ".api.txt"
+ tag: ".api.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "removed.txt",
tag: ".removed-api.txt",
},
- ]
+ ],
}
java_library {
@@ -223,16 +233,22 @@
},
dists: [
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "android-non-updatable.txt",
- tag: ".api.txt"
+ tag: ".api.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "android-non-updatable-removed.txt",
tag: ".removed-api.txt",
},
- ]
-}
\ No newline at end of file
+ ],
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 5b74cbd..1f66bfd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -79,7 +79,7 @@
private final ScreenStateObserver mScreenStateObserver;
- private final MagnificationRequestObserver mMagnificationRequestObserver;
+ private final MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
private int mUserId;
@@ -284,6 +284,14 @@
mControllerCtx.getHandler().sendMessage(m);
}
+ @Override
+ public void onImeWindowVisibilityChanged(boolean shown) {
+ final Message m = PooledLambda.obtainMessage(
+ FullScreenMagnificationController::notifyImeWindowVisibilityChanged,
+ FullScreenMagnificationController.this, shown);
+ mControllerCtx.getHandler().sendMessage(m);
+ }
+
/**
* Update our copy of the current magnification region
*
@@ -329,7 +337,7 @@
final boolean lastMagnificationActivated = mMagnificationActivated;
mMagnificationActivated = spec.scale > 1.0f;
if (mMagnificationActivated != lastMagnificationActivated) {
- mMagnificationRequestObserver.onFullScreenMagnificationActivationState(
+ mMagnificationInfoChangedCallback.onFullScreenMagnificationActivationState(
mMagnificationActivated);
}
}
@@ -498,7 +506,7 @@
sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback);
if (isMagnifying() && (id != INVALID_ID)) {
mIdOfLastServiceToMagnify = id;
- mMagnificationRequestObserver.onRequestMagnificationSpec(mDisplayId,
+ mMagnificationInfoChangedCallback.onRequestMagnificationSpec(mDisplayId,
mIdOfLastServiceToMagnify);
}
return changed;
@@ -631,12 +639,12 @@
*/
public FullScreenMagnificationController(@NonNull Context context,
@NonNull AccessibilityManagerService ams, @NonNull Object lock,
- @NonNull MagnificationRequestObserver magnificationRequestObserver) {
+ @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) {
this(new ControllerContext(context, ams,
LocalServices.getService(WindowManagerInternal.class),
new Handler(context.getMainLooper()),
context.getResources().getInteger(R.integer.config_longAnimTime)), lock,
- magnificationRequestObserver);
+ magnificationInfoChangedCallback);
}
/**
@@ -645,12 +653,12 @@
@VisibleForTesting
public FullScreenMagnificationController(@NonNull ControllerContext ctx,
@NonNull Object lock,
- @NonNull MagnificationRequestObserver magnificationRequestObserver) {
+ @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) {
mControllerCtx = ctx;
mLock = lock;
mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this);
- mMagnificationRequestObserver = magnificationRequestObserver;
+ mMagnificationInfoChangedCallback = magnificationInfoChangedCallback;
}
/**
@@ -1168,6 +1176,16 @@
}
/**
+ * Notifies that the IME window visibility changed.
+ *
+ * @param shown {@code true} means the IME window shows on the screen. Otherwise it's
+ * hidden.
+ */
+ void notifyImeWindowVisibilityChanged(boolean shown) {
+ mMagnificationInfoChangedCallback.onImeWindowVisibilityChanged(shown);
+ }
+
+ /**
* Returns {@code true} if the magnifiable regions of the display is forced to be shown.
*
* @param displayId The logical display id.
@@ -1528,7 +1546,7 @@
return animate ? STUB_ANIMATION_CALLBACK : null;
}
- interface MagnificationRequestObserver {
+ interface MagnificationInfoChangedCallback {
/**
* Called when the {@link MagnificationSpec} is changed with non-default
@@ -1545,7 +1563,13 @@
*
* @param activated {@code true} if the magnification is activated, otherwise {@code false}.
*/
- @GuardedBy("mLock")
void onFullScreenMagnificationActivationState(boolean activated);
+
+ /**
+ * Called when the IME window visibility changed.
+ * @param shown {@code true} means the IME window shows on the screen. Otherwise it's
+ * hidden.
+ */
+ void onImeWindowVisibilityChanged(boolean shown);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 2073c70..878ebc5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -18,6 +18,7 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
import android.annotation.NonNull;
@@ -33,6 +34,7 @@
import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
@@ -58,7 +60,7 @@
*/
public class MagnificationController implements WindowMagnificationManager.Callback,
MagnificationGestureHandler.Callback,
- FullScreenMagnificationController.MagnificationRequestObserver {
+ FullScreenMagnificationController.MagnificationInfoChangedCallback {
private static final boolean DEBUG = false;
private static final String TAG = "MagnificationController";
@@ -73,6 +75,10 @@
private WindowMagnificationManager mWindowMagnificationMgr;
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ @GuardedBy("mLock")
+ private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ @GuardedBy("mLock")
+ private boolean mImeWindowVisible = false;
private long mWindowModeEnabledTime = 0;
private long mFullScreenModeEnabledTime = 0;
@@ -216,9 +222,18 @@
public void onWindowMagnificationActivationState(boolean activated) {
if (activated) {
mWindowModeEnabledTime = SystemClock.uptimeMillis();
+
+ synchronized (mLock) {
+ mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+ }
+ logMagnificationModeWithImeOnIfNeeded();
} else {
logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
SystemClock.uptimeMillis() - mWindowModeEnabledTime);
+
+ synchronized (mLock) {
+ mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ }
}
}
@@ -226,12 +241,29 @@
public void onFullScreenMagnificationActivationState(boolean activated) {
if (activated) {
mFullScreenModeEnabledTime = SystemClock.uptimeMillis();
+
+ synchronized (mLock) {
+ mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ }
+ logMagnificationModeWithImeOnIfNeeded();
} else {
logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
SystemClock.uptimeMillis() - mFullScreenModeEnabledTime);
+
+ synchronized (mLock) {
+ mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ }
}
}
+ @Override
+ public void onImeWindowVisibilityChanged(boolean shown) {
+ synchronized (mLock) {
+ mImeWindowVisible = shown;
+ }
+ logMagnificationModeWithImeOnIfNeeded();
+ }
+
/**
* Wrapper method of logging the magnification activated mode and its duration of the usage
* when the magnification is disabled.
@@ -245,6 +277,17 @@
}
/**
+ * Wrapper method of logging the activated mode of the magnification when the IME window
+ * is shown on the screen.
+ *
+ * @param mode The activated magnification mode.
+ */
+ @VisibleForTesting
+ public void logMagnificationModeWithIme(int mode) {
+ AccessibilityStatsLogUtils.logMagnificationModeWithImeOn(mode);
+ }
+
+ /**
* Updates the active user ID of {@link FullScreenMagnificationController} and {@link
* WindowMagnificationManager}.
*
@@ -295,6 +338,18 @@
}
}
+ private void logMagnificationModeWithImeOnIfNeeded() {
+ final int mode;
+
+ synchronized (mLock) {
+ if (!mImeWindowVisible || mActivatedMode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) {
+ return;
+ }
+ mode = mActivatedMode;
+ }
+ logMagnificationModeWithIme(mode);
+ }
+
/**
* Getter of {@link FullScreenMagnificationController}.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
index d2c1bc1..1fdd908 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
@@ -108,7 +108,7 @@
SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION);
final String message = mContext.getString(R.string.window_magnification_prompt_content);
- notificationBuilder.setSmallIcon(R.drawable.ic_settings_24dp)
+ notificationBuilder.setSmallIcon(R.drawable.ic_accessibility_24dp)
.setContentTitle(mContext.getString(R.string.window_magnification_prompt_title))
.setContentText(message)
.setLargeIcon(Icon.createWithResource(mContext,
@@ -118,7 +118,7 @@
.setStyle(new Notification.BigTextStyle().bigText(message))
.setDeleteIntent(createPendingIntent(ACTION_DISMISS))
.setContentIntent(createPendingIntent(ACTION_TURN_ON_IN_SETTINGS))
- .setActions(buildTurnOnAction(), buildDismissAction());
+ .setActions(buildTurnOnAction());
mNotificationManager.notify(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE,
notificationBuilder.build());
registerReceiverIfNeeded();
@@ -145,11 +145,6 @@
createPendingIntent(ACTION_TURN_ON_IN_SETTINGS)).build();
}
- private Notification.Action buildDismissAction() {
- return new Notification.Action.Builder(null, mContext.getString(R.string.dismiss_action),
- createPendingIntent(ACTION_DISMISS)).build();
- }
-
private PendingIntent createPendingIntent(String action) {
final Intent intent = new Intent(action);
intent.setPackage(mContext.getPackageName());
diff --git a/services/api/current.txt b/services/api/current.txt
index a3e6715..a0b1e33 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -105,6 +105,14 @@
}
+package com.android.server.stats {
+
+ public final class StatsHelper {
+ method public static void sendStatsdReadyBroadcast(@NonNull android.content.Context);
+ }
+
+}
+
package com.android.server.wifi {
public class SupplicantManager {
diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt
index f01c182..475dcf5 100644
--- a/services/api/non-updatable-current.txt
+++ b/services/api/non-updatable-current.txt
@@ -52,6 +52,14 @@
}
+package com.android.server.stats {
+
+ public final class StatsHelper {
+ method public static void sendStatsdReadyBroadcast(@NonNull android.content.Context);
+ }
+
+}
+
package com.android.server.wifi {
public class SupplicantManager {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index a1ad72c..312cde6 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -25,6 +25,8 @@
import static com.android.server.autofill.Helper.sFullScreenMode;
import static com.android.server.autofill.Helper.sVerbose;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -1370,15 +1372,16 @@
}
@Override
- public void startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId,
- Rect bounds, AutofillValue value, int userId, boolean hasCallback, int flags,
- ComponentName componentName, boolean compatMode, IResultReceiver receiver) {
+ public void startSession(IBinder activityToken, IBinder clientCallback,
+ AutofillId autofillId, Rect bounds, AutofillValue value, int userId,
+ boolean hasCallback, int flags, ComponentName clientActivity,
+ boolean compatMode, IResultReceiver receiver) {
- activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
- appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
- autofillId = Preconditions.checkNotNull(autofillId, "autoFillId");
- componentName = Preconditions.checkNotNull(componentName, "componentName");
- final String packageName = Preconditions.checkNotNull(componentName.getPackageName());
+ requireNonNull(activityToken, "activityToken");
+ requireNonNull(clientCallback, "clientCallback");
+ requireNonNull(autofillId, "autofillId");
+ requireNonNull(clientActivity, "clientActivity");
+ final String packageName = requireNonNull(clientActivity.getPackageName());
Preconditions.checkArgument(userId == UserHandle.getUserId(getCallingUid()), "userId");
@@ -1395,7 +1398,7 @@
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
result = service.startSessionLocked(activityToken, taskId, getCallingUid(),
- appCallback, autofillId, bounds, value, hasCallback, componentName,
+ clientCallback, autofillId, bounds, value, hasCallback, clientActivity,
compatMode, mAllowInstantService, flags);
}
final int sessionId = (int) result;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index b5f4813..27be331 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -307,10 +307,10 @@
* {@link AutofillManager#RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY}).
*/
@GuardedBy("mLock")
- long startSessionLocked(@NonNull IBinder activityToken, int taskId, int uid,
- @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
+ long startSessionLocked(@NonNull IBinder activityToken, int taskId, int clientUid,
+ @NonNull IBinder clientCallback, @NonNull AutofillId autofillId,
@NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
- @NonNull ComponentName componentName, boolean compatMode,
+ @NonNull ComponentName clientActivity, boolean compatMode,
boolean bindInstantServiceAllowed, int flags) {
// FLAG_AUGMENTED_AUTOFILL_REQUEST is set in the flags when standard autofill is disabled
// but the package is allowlisted for augmented autofill
@@ -320,29 +320,29 @@
return 0;
}
- if (!forAugmentedAutofillOnly && isAutofillDisabledLocked(componentName)) {
+ if (!forAugmentedAutofillOnly && isAutofillDisabledLocked(clientActivity)) {
// Standard autofill is enabled, but service disabled autofill for this activity; that
// means no session, unless the activity is allowlisted for augmented autofill
- if (isWhitelistedForAugmentedAutofillLocked(componentName)) {
+ if (isWhitelistedForAugmentedAutofillLocked(clientActivity)) {
if (sDebug) {
- Slog.d(TAG, "startSession(" + componentName + "): disabled by service but "
+ Slog.d(TAG, "startSession(" + clientActivity + "): disabled by service but "
+ "whitelisted for augmented autofill");
}
forAugmentedAutofillOnly = true;
} else {
if (sDebug) {
- Slog.d(TAG, "startSession(" + componentName + "): ignored because "
+ Slog.d(TAG, "startSession(" + clientActivity + "): ignored because "
+ "disabled by service and not whitelisted for augmented autofill");
}
final IAutoFillManagerClient client = IAutoFillManagerClient.Stub
- .asInterface(appCallbackToken);
+ .asInterface(clientCallback);
try {
client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE,
/* autofillableIds= */ null);
} catch (RemoteException e) {
Slog.w(TAG,
- "Could not notify " + componentName + " that it's disabled: " + e);
+ "Could not notify " + clientActivity + " that it's disabled: " + e);
}
return NO_SESSION;
@@ -357,8 +357,8 @@
// Occasionally clean up abandoned sessions
pruneAbandonedSessionsLocked();
- final Session newSession = createSessionByTokenLocked(activityToken, taskId, uid,
- appCallbackToken, hasCallback, componentName, compatMode,
+ final Session newSession = createSessionByTokenLocked(activityToken, taskId, clientUid,
+ clientCallback, hasCallback, clientActivity, compatMode,
bindInstantServiceAllowed, forAugmentedAutofillOnly, flags);
if (newSession == null) {
return NO_SESSION;
@@ -367,7 +367,7 @@
// Service can be null when it's only for augmented autofill
String servicePackageName = mInfo == null ? null : mInfo.getServiceInfo().packageName;
final String historyItem =
- "id=" + newSession.id + " uid=" + uid + " a=" + componentName.toShortString()
+ "id=" + newSession.id + " uid=" + clientUid + " a=" + clientActivity.toShortString()
+ " s=" + servicePackageName
+ " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds
+ " hc=" + hasCallback + " f=" + flags + " aa=" + forAugmentedAutofillOnly;
@@ -493,9 +493,9 @@
}
@GuardedBy("mLock")
- private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int taskId, int uid,
- @NonNull IBinder appCallbackToken, boolean hasCallback,
- @NonNull ComponentName componentName, boolean compatMode,
+ private Session createSessionByTokenLocked(@NonNull IBinder clientActivityToken, int taskId,
+ int clientUid, @NonNull IBinder clientCallback, boolean hasCallback,
+ @NonNull ComponentName clientActivity, boolean compatMode,
boolean bindInstantServiceAllowed, boolean forAugmentedAutofillOnly, int flags) {
// use random ids so that one app cannot know that another app creates sessions
int sessionId;
@@ -511,15 +511,15 @@
} while (sessionId == 0 || sessionId == NO_SESSION
|| mSessions.indexOfKey(sessionId) >= 0);
- assertCallerLocked(componentName, compatMode);
+ assertCallerLocked(clientActivity, compatMode);
// It's null when the session is just for augmented autofill
final ComponentName serviceComponentName = mInfo == null ? null
: mInfo.getServiceInfo().getComponentName();
final Session newSession = new Session(this, mUi, getContext(), mHandler, mUserId, mLock,
- sessionId, taskId, uid, activityToken, appCallbackToken, hasCallback,
+ sessionId, taskId, clientUid, clientActivityToken, clientCallback, hasCallback,
mUiLatencyHistory, mWtfHistory, serviceComponentName,
- componentName, compatMode, bindInstantServiceAllowed, forAugmentedAutofillOnly,
+ clientActivity, compatMode, bindInstantServiceAllowed, forAugmentedAutofillOnly,
flags, mInputMethodManagerInternal);
mSessions.put(newSession.id, newSession);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index b7f736e..a230f80 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -196,7 +196,7 @@
/** userId the session belongs to */
public final int userId;
- /** uid the session is for */
+ /** The uid of the app that's being autofilled */
public final int uid;
/** ID of the task associated with this session's activity */
@@ -208,7 +208,7 @@
@GuardedBy("mLock")
@NonNull private IBinder mActivityToken;
- /** Component that's being auto-filled */
+ /** The app activity that's being autofilled */
@NonNull private final ComponentName mComponentName;
/** Whether the app being autofilled is running in compat mode. */
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b160d78..483f67a 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -639,6 +639,16 @@
}));
}
+ @Override
+ public boolean createAssociation(String packageName, String macAddress, int userId) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES, "createAssociation");
+
+ addAssociation(new Association(
+ userId, macAddress, packageName, null, false, System.currentTimeMillis()));
+ return true;
+ }
+
private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
checkCallerIsSystemOr(callingPackage);
int userId = getCallingUserId();
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 641b38d..1e3f12a 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -158,7 +158,7 @@
"android.hardware.power.stats-V1-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
- "dnsresolver_aidl_interface-V7-java",
+ "dnsresolver_aidl_interface-V8-java",
"icu4j_calendar_astronomer",
"netd-client",
"overlayable_policy_aidl-java",
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index f0c9ba9..09cfac0 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -16,7 +16,10 @@
package com.android.server;
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.content.PermissionChecker.PERMISSION_HARD_DENIED;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_SYSTEM;
import android.Manifest;
@@ -43,6 +46,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.PermissionChecker;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -95,8 +99,6 @@
private static final String TAG = "BluetoothManagerService";
private static final boolean DBG = true;
- private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
- private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
private static final String BLUETOOTH_PRIVILEGED =
android.Manifest.permission.BLUETOOTH_PRIVILEGED;
@@ -696,14 +698,18 @@
Slog.w(TAG, "Callback is null in unregisterAdapter");
return;
}
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (!checkConnectPermissionForPreflight(mContext)) {
+ return;
+ }
synchronized (mCallbacks) {
mCallbacks.unregister(callback);
}
}
public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (!checkConnectPermissionForPreflight(mContext)) {
+ return;
+ }
if (callback == null) {
Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
return;
@@ -714,7 +720,9 @@
}
public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (!checkConnectPermissionForPreflight(mContext)) {
+ return;
+ }
if (callback == null) {
Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
return;
@@ -945,8 +953,9 @@
return false;
}
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH ADMIN permission");
+ if (!checkConnectPermissionForPreflight(mContext)) {
+ return false;
+ }
}
return true;
}
@@ -1672,7 +1681,9 @@
}
public String getAddress() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (!checkConnectPermissionForPreflight(mContext)) {
+ return null;
+ }
if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
Slog.w(TAG, "getAddress(): not allowed for non-active and non system user");
@@ -1704,7 +1715,9 @@
}
public String getName() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (!checkConnectPermissionForPreflight(mContext)) {
+ return null;
+ }
if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
Slog.w(TAG, "getName(): not allowed for non-active and non system user");
@@ -2459,7 +2472,7 @@
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT);
}
private void bluetoothStateChangeHandler(int prevState, int newState) {
@@ -2538,7 +2551,7 @@
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT);
}
}
@@ -2827,4 +2840,20 @@
default: return "UNKNOWN[" + reason + "]";
}
}
+
+ /**
+ * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns
+ * false if the result is a soft denial. Throws SecurityException if the result is a hard
+ * denial.
+ *
+ * <p>Should be used in situations where the app op should not be noted.
+ */
+ private static boolean checkConnectPermissionForPreflight(Context context) {
+ int permissionCheckResult = PermissionChecker.checkCallingOrSelfPermissionForPreflight(
+ context, BLUETOOTH_CONNECT);
+ if (permissionCheckResult == PERMISSION_HARD_DENIED) {
+ throw new SecurityException("Need BLUETOOTH_CONNECT permission");
+ }
+ return permissionCheckResult == PERMISSION_GRANTED;
+ }
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8f56842..ca01c8e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -29,6 +29,8 @@
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
+import static android.net.ConnectivityManager.BLOCKED_REASON_LOCKDOWN_VPN;
import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
@@ -106,6 +108,7 @@
import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.BlockedReason;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.RestrictBackgroundStatus;
import android.net.ConnectivityResources;
@@ -1567,16 +1570,16 @@
mNetworkInfoBlockingLogs.log(action + " " + uid);
}
- private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net,
- boolean blocked) {
+ private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net, int blocked) {
if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
return;
}
- final String action = blocked ? "BLOCKED" : "UNBLOCKED";
+ final String action = (blocked != 0) ? "BLOCKED" : "UNBLOCKED";
final int requestId = nri.getActiveRequest() != null
? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId;
mNetworkInfoBlockingLogs.log(String.format(
- "%s %d(%d) on netId %d", action, nri.mAsUid, requestId, net.getNetId()));
+ "%s %d(%d) on netId %d: %s", action, nri.mAsUid, requestId, net.getNetId(),
+ blockedReasonsToString(blocked)));
}
/**
@@ -2357,15 +2360,15 @@
private final NetworkPolicyCallback mPolicyCallback = new NetworkPolicyCallback() {
@Override
- public void onUidBlockedReasonChanged(int uid, int blockedReasons) {
+ public void onUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_BLOCKED_REASON_CHANGED,
uid, blockedReasons));
}
};
- void handleUidBlockedReasonChanged(int uid, int blockedReasons) {
+ private void handleUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {
maybeNotifyNetworkBlockedForNewState(uid, blockedReasons);
- mUidBlockedReasons.put(uid, blockedReasons);
+ setUidBlockedReasons(uid, blockedReasons);
}
private boolean checkAnyPermissionOf(String... permissions) {
@@ -3134,6 +3137,13 @@
}
break;
}
+ case NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED: {
+ if (msg.arg1 >= 0 && msg.arg1 <= NetworkAgent.MAX_TEARDOWN_DELAY_MS) {
+ nai.teardownDelayMs = msg.arg1;
+ } else {
+ logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1);
+ }
+ }
}
}
@@ -3259,8 +3269,6 @@
nai.lastValidated = valid;
nai.everValidated |= valid;
updateCapabilities(oldScore, nai, nai.networkCapabilities);
- // If score has changed, rebroadcast to NetworkProviders. b/17726566
- if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
if (valid) {
handleFreshlyValidatedNetwork(nai);
// Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
@@ -3681,9 +3689,12 @@
if (currentNetwork != null
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
// uid rules for this network will be removed in destroyNativeNetwork(nai).
+ // TODO : setting the satisfier is in fact the job of the rematch. Teach the
+ // rematch not to keep disconnected agents instead of setting it here ; this
+ // will also allow removing updating the offers below.
nri.setSatisfier(null, null);
- if (request.isRequest()) {
- sendUpdatedScoreToFactories(request, null);
+ for (final NetworkOfferInfo noi : mNetworkOffers) {
+ informOffer(nri, noi.offer, mNetworkRanker);
}
if (mDefaultRequest == nri) {
@@ -3704,6 +3715,23 @@
mLegacyTypeTracker.remove(nai, wasDefault);
rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
+
+ // Immediate teardown.
+ if (nai.teardownDelayMs == 0) {
+ destroyNetwork(nai);
+ return;
+ }
+
+ // Delayed teardown.
+ try {
+ mNetd.networkSetPermissionForNetwork(nai.network.netId, INetd.PERMISSION_SYSTEM);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error marking network restricted during teardown: " + e);
+ }
+ mHandler.postDelayed(() -> destroyNetwork(nai), nai.teardownDelayMs);
+ }
+
+ private void destroyNetwork(NetworkAgentInfo nai) {
if (nai.created) {
// Tell netd to clean up the configuration for this network
// (routing rules, DNS, etc).
@@ -3716,6 +3744,7 @@
mDnsManager.removeNetwork(nai.network);
}
mNetIdManager.releaseNetId(nai.network.getNetId());
+ nai.onNetworkDestroyed();
}
private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
@@ -3810,16 +3839,13 @@
}
rematchAllNetworksAndRequests();
- for (final NetworkRequestInfo nri : nris) {
- // If the nri is satisfied, return as its score has already been sent if needed.
- if (nri.isBeingSatisfied()) {
- return;
- }
- // As this request was not satisfied on rematch and thus never had any scores sent to
- // the factories, send null now for each request of type REQUEST.
- for (final NetworkRequest req : nri.mRequests) {
- if (req.isRequest()) sendUpdatedScoreToFactories(req, null);
+ // Requests that have not been matched to a network will not have been sent to the
+ // providers, because the old satisfier and the new satisfier are the same (null in this
+ // case). Send these requests to the providers.
+ for (final NetworkRequestInfo nri : nris) {
+ for (final NetworkOfferInfo noi : mNetworkOffers) {
+ informOffer(nri, noi.offer, mNetworkRanker);
}
}
}
@@ -3847,6 +3873,12 @@
// then it should be lingered.
private boolean unneeded(NetworkAgentInfo nai, UnneededFor reason) {
ensureRunningOnConnectivityServiceThread();
+
+ if (!nai.everConnected || nai.isVPN() || nai.isInactive()
+ || nai.getScore().getKeepConnectedReason() != NetworkScore.KEEP_CONNECTED_NONE) {
+ return false;
+ }
+
final int numRequests;
switch (reason) {
case TEARDOWN:
@@ -3860,9 +3892,8 @@
return true;
}
- if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) {
- return false;
- }
+ if (numRequests > 0) return false;
+
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (reason == UnneededFor.LINGER
&& !nri.isMultilayerRequest()
@@ -4027,7 +4058,15 @@
}
}
- cancelNpiRequests(nri);
+ // For all outstanding offers, cancel any of the layers of this NRI that used to be
+ // needed for this offer.
+ for (final NetworkOfferInfo noi : mNetworkOffers) {
+ for (final NetworkRequest req : nri.mRequests) {
+ if (req.isRequest() && noi.offer.neededFor(req)) {
+ noi.offer.onNetworkUnneeded(req);
+ }
+ }
+ }
}
private void handleRemoveNetworkRequests(@NonNull final Set<NetworkRequestInfo> nris) {
@@ -4040,20 +4079,6 @@
}
}
- private void cancelNpiRequests(@NonNull final NetworkRequestInfo nri) {
- for (final NetworkRequest req : nri.mRequests) {
- cancelNpiRequest(req);
- }
- }
-
- private void cancelNpiRequest(@NonNull final NetworkRequest req) {
- if (req.isRequest()) {
- for (final NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
- npi.cancelRequest(req);
- }
- }
- }
-
private void removeListenRequestFromNetworks(@NonNull final NetworkRequest req) {
// listens don't have a singular affected Network. Check all networks to see
// if this listen request applies and remove it.
@@ -4174,7 +4199,6 @@
nai.networkAgentConfig.acceptPartialConnectivity = accept;
nai.updateScoreForNetworkAgentConfigUpdate();
rematchAllNetworksAndRequests();
- sendUpdatedScoreToFactories(nai);
}
if (always) {
@@ -4242,7 +4266,6 @@
if (!nai.avoidUnvalidated) {
nai.avoidUnvalidated = true;
rematchAllNetworksAndRequests();
- sendUpdatedScoreToFactories(nai);
}
}
@@ -4347,14 +4370,9 @@
return avoidBadWifi();
}
-
+ // TODO : this function is now useless.
private void rematchForAvoidBadWifiUpdate() {
rematchAllNetworksAndRequests();
- for (NetworkAgentInfo nai: mNetworkAgentInfos) {
- if (nai.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
- sendUpdatedScoreToFactories(nai);
- }
- }
}
// TODO: Evaluate whether this is of interest to other consumers of
@@ -5348,24 +5366,6 @@
}
}
- void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
- try {
- messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
- } catch (RemoteException e) {
- // Remote process died. Ignore; the death recipient will remove this
- // NetworkProviderInfo from mNetworkProviderInfos.
- }
- }
-
- void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
- sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
- servingProviderId, request);
- }
-
- void cancelRequest(NetworkRequest request) {
- sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
- }
-
void connect(Context context, Handler handler) {
try {
messenger.getBinder().linkToDeath(mDeathRecipient, 0);
@@ -6054,7 +6054,6 @@
if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
mNetworkProviderInfos.put(npi.messenger, npi);
npi.connect(mContext, mTrackerHandler);
- sendAllRequestsToProvider(npi);
}
@Override
@@ -6076,6 +6075,9 @@
public void offerNetwork(final int providerId,
@NonNull final NetworkScore score, @NonNull final NetworkCapabilities caps,
@NonNull final INetworkOfferCallback callback) {
+ Objects.requireNonNull(score);
+ Objects.requireNonNull(caps);
+ Objects.requireNonNull(callback);
final NetworkOffer offer = new NetworkOffer(
FullScore.makeProspectiveScore(score, caps), caps, callback, providerId);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_OFFER, offer));
@@ -6100,7 +6102,7 @@
toRemove.add(noi);
}
}
- for (NetworkOfferInfo noi : toRemove) {
+ for (final NetworkOfferInfo noi : toRemove) {
handleUnregisterNetworkOffer(noi);
}
if (DBG) log("unregisterNetworkProvider for " + npi.name);
@@ -6501,7 +6503,7 @@
return;
}
mNetworkOffers.add(noi);
- // TODO : send requests to the provider.
+ issueNetworkNeeds(noi);
}
private void handleUnregisterNetworkOffer(@NonNull final NetworkOfferInfo noi) {
@@ -7276,100 +7278,6 @@
updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
}
- private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
- for (int i = 0; i < nai.numNetworkRequests(); i++) {
- NetworkRequest nr = nai.requestAt(i);
- // Don't send listening or track default request to factories. b/17393458
- if (!nr.isRequest()) continue;
- sendUpdatedScoreToFactories(nr, nai);
- }
- }
-
- private void sendUpdatedScoreToFactories(
- @NonNull final NetworkReassignment.RequestReassignment event) {
- // If a request of type REQUEST is now being satisfied by a new network.
- if (null != event.mNewNetworkRequest && event.mNewNetworkRequest.isRequest()) {
- sendUpdatedScoreToFactories(event.mNewNetworkRequest, event.mNewNetwork);
- }
-
- // If a previously satisfied request of type REQUEST is no longer being satisfied.
- if (null != event.mOldNetworkRequest && event.mOldNetworkRequest.isRequest()
- && event.mOldNetworkRequest != event.mNewNetworkRequest) {
- sendUpdatedScoreToFactories(event.mOldNetworkRequest, null);
- }
-
- cancelMultilayerLowerPriorityNpiRequests(event.mNetworkRequestInfo);
- }
-
- /**
- * Cancel with all NPIs the given NRI's multilayer requests that are a lower priority than
- * its currently satisfied active request.
- * @param nri the NRI to cancel lower priority requests for.
- */
- private void cancelMultilayerLowerPriorityNpiRequests(
- @NonNull final NetworkRequestInfo nri) {
- if (!nri.isMultilayerRequest() || null == nri.mActiveRequest) {
- return;
- }
-
- final int indexOfNewRequest = nri.mRequests.indexOf(nri.mActiveRequest);
- for (int i = indexOfNewRequest + 1; i < nri.mRequests.size(); i++) {
- cancelNpiRequest(nri.mRequests.get(i));
- }
- }
-
- private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest,
- @Nullable NetworkAgentInfo nai) {
- final int score;
- final int serial;
- if (nai != null) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = 0;
- }
- if (VDBG || DDBG){
- log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
- }
- for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
- npi.requestNetwork(networkRequest, score, serial);
- }
- }
-
- /** Sends all current NetworkRequests to the specified factory. */
- private void sendAllRequestsToProvider(@NonNull final NetworkProviderInfo npi) {
- ensureRunningOnConnectivityServiceThread();
- for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
- for (final NetworkRequest req : nri.mRequests) {
- if (!req.isRequest() && nri.getActiveRequest() == req) {
- break;
- }
- if (!req.isRequest()) {
- continue;
- }
- // Only set the nai for the request it is satisfying.
- final NetworkAgentInfo nai =
- nri.getActiveRequest() == req ? nri.getSatisfier() : null;
- final int score;
- final int serial;
- if (null != nai) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = NetworkProvider.ID_NONE;
- }
- npi.requestNetwork(req, score, serial);
- // For multilayer requests, don't send lower priority requests if a higher priority
- // request is already satisfied.
- if (null != nai) {
- break;
- }
- }
- }
- }
-
private void sendPendingIntentForRequest(NetworkRequestInfo nri, NetworkAgentInfo networkAgent,
int notificationType) {
if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
@@ -7470,7 +7378,7 @@
break;
}
case ConnectivityManager.CALLBACK_BLK_CHANGED: {
- maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1 != 0);
+ maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1);
msg.arg1 = arg1;
break;
}
@@ -7861,6 +7769,7 @@
log(changes.toString()); // Shorter form, only one line of log
}
applyNetworkReassignment(changes, now);
+ issueNetworkNeeds();
}
private void applyNetworkReassignment(@NonNull final NetworkReassignment changes,
@@ -7892,12 +7801,6 @@
// before LegacyTypeTracker sends legacy broadcasts
for (final NetworkReassignment.RequestReassignment event :
changes.getRequestReassignments()) {
- // Tell NetworkProviders about the new score, so they can stop
- // trying to connect if they know they cannot match it.
- // TODO - this could get expensive if there are a lot of outstanding requests for this
- // network. Think of a way to reduce this. Push netid->request mapping to each factory?
- sendUpdatedScoreToFactories(event);
-
if (null != event.mNewNetwork) {
notifyNetworkAvailable(event.mNewNetwork, event.mNetworkRequestInfo);
} else {
@@ -8034,6 +7937,107 @@
}
}
+ private void issueNetworkNeeds() {
+ ensureRunningOnConnectivityServiceThread();
+ for (final NetworkOfferInfo noi : mNetworkOffers) {
+ issueNetworkNeeds(noi);
+ }
+ }
+
+ private void issueNetworkNeeds(@NonNull final NetworkOfferInfo noi) {
+ ensureRunningOnConnectivityServiceThread();
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ informOffer(nri, noi.offer, mNetworkRanker);
+ }
+ }
+
+ /**
+ * Inform a NetworkOffer about any new situation of a request.
+ *
+ * This function handles updates to offers. A number of events may happen that require
+ * updating the registrant for this offer about the situation :
+ * • The offer itself was updated. This may lead the offer to no longer being able
+ * to satisfy a request or beat a satisfier (and therefore be no longer needed),
+ * or conversely being strengthened enough to beat the satisfier (and therefore
+ * start being needed)
+ * • The network satisfying a request changed (including cases where the request
+ * starts or stops being satisfied). The new network may be a stronger or weaker
+ * match than the old one, possibly affecting whether the offer is needed.
+ * • The network satisfying a request updated their score. This may lead the offer
+ * to no longer be able to beat it if the current satisfier got better, or
+ * conversely start being a good choice if the current satisfier got weaker.
+ *
+ * @param nri The request
+ * @param offer The offer. This may be an updated offer.
+ */
+ private static void informOffer(@NonNull NetworkRequestInfo nri,
+ @NonNull final NetworkOffer offer, @NonNull final NetworkRanker networkRanker) {
+ final NetworkRequest activeRequest = nri.isBeingSatisfied() ? nri.getActiveRequest() : null;
+ final NetworkAgentInfo satisfier = null != activeRequest ? nri.getSatisfier() : null;
+ final FullScore satisfierScore = null != satisfier ? satisfier.getScore() : null;
+
+ // Multi-layer requests have a currently active request, the one being satisfied.
+ // Since the system will try to bring up a better network than is currently satisfying
+ // the request, NetworkProviders need to be told the offers matching the requests *above*
+ // the currently satisfied one are needed, that the ones *below* the satisfied one are
+ // not needed, and the offer is needed for the active request iff the offer can beat
+ // the satisfier.
+ // For non-multilayer requests, the logic above gracefully degenerates to only the
+ // last case.
+ // To achieve this, the loop below will proceed in three steps. In a first phase, inform
+ // providers that the offer is needed for this request, until the active request is found.
+ // In a second phase, deal with the currently active request. In a third phase, inform
+ // the providers that offer is unneeded for the remaining requests.
+
+ // First phase : inform providers of all requests above the active request.
+ int i;
+ for (i = 0; nri.mRequests.size() > i; ++i) {
+ final NetworkRequest request = nri.mRequests.get(i);
+ if (activeRequest == request) break; // Found the active request : go to phase 2
+ if (!request.isRequest()) continue; // Listens/track defaults are never sent to offers
+ // Since this request is higher-priority than the one currently satisfied, if the
+ // offer can satisfy it, the provider should try and bring up the network for sure ;
+ // no need to even ask the ranker – an offer that can satisfy is always better than
+ // no network. Hence tell the provider so unless it already knew.
+ if (request.canBeSatisfiedBy(offer.caps) && !offer.neededFor(request)) {
+ offer.onNetworkNeeded(request);
+ }
+ }
+
+ // Second phase : deal with the active request (if any)
+ if (null != activeRequest && activeRequest.isRequest()) {
+ final boolean oldNeeded = offer.neededFor(activeRequest);
+ // An offer is needed if it is currently served by this provider or if this offer
+ // can beat the current satisfier.
+ final boolean currentlyServing = satisfier != null
+ && satisfier.factorySerialNumber == offer.providerId;
+ final boolean newNeeded = (currentlyServing
+ || (activeRequest.canBeSatisfiedBy(offer.caps)
+ && networkRanker.mightBeat(activeRequest, satisfierScore, offer)));
+ if (newNeeded != oldNeeded) {
+ if (newNeeded) {
+ offer.onNetworkNeeded(activeRequest);
+ } else {
+ // The offer used to be able to beat the satisfier. Now it can't.
+ offer.onNetworkUnneeded(activeRequest);
+ }
+ }
+ }
+
+ // Third phase : inform the providers that the offer isn't needed for any request
+ // below the active one.
+ for (++i /* skip the active request */; nri.mRequests.size() > i; ++i) {
+ final NetworkRequest request = nri.mRequests.get(i);
+ if (!request.isRequest()) continue; // Listens/track defaults are never sent to offers
+ // Since this request is lower-priority than the one currently satisfied, if the
+ // offer can satisfy it, the provider should not try and bring up the network.
+ // Hence tell the provider so unless it already knew.
+ if (offer.neededFor(request)) {
+ offer.onNetworkUnneeded(request);
+ }
+ }
+ }
+
private void addNetworkToLegacyTypeTracker(@NonNull final NetworkAgentInfo nai) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
@@ -8126,6 +8130,7 @@
updateCapabilitiesForNetwork(networkAgent);
}
networkAgent.created = true;
+ networkAgent.onNetworkCreated();
}
if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
@@ -8198,7 +8203,6 @@
if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + score);
nai.setScore(score);
rematchAllNetworksAndRequests();
- sendUpdatedScoreToFactories(nai);
}
// Notify only this one new request of the current state. Transfer all the
@@ -8213,12 +8217,11 @@
return;
}
+ final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
final boolean metered = nai.networkCapabilities.isMetered();
- boolean blocked;
- blocked = isUidBlockedByVpn(nri.mAsUid, mVpnBlockedUidRanges);
- blocked |= NetworkPolicyManager.isUidBlocked(
- mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE), metered);
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
+ final boolean vpnBlocked = isUidBlockedByVpn(nri.mAsUid, mVpnBlockedUidRanges);
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE,
+ getBlockedState(blockedReasons, metered, vpnBlocked));
}
// Notify the requests on this NAI that the network is now lingered.
@@ -8227,6 +8230,21 @@
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
}
+ private static int getBlockedState(int reasons, boolean metered, boolean vpnBlocked) {
+ if (!metered) reasons &= ~BLOCKED_METERED_REASON_MASK;
+ return vpnBlocked
+ ? reasons | BLOCKED_REASON_LOCKDOWN_VPN
+ : reasons & ~BLOCKED_REASON_LOCKDOWN_VPN;
+ }
+
+ private void setUidBlockedReasons(int uid, @BlockedReason int blockedReasons) {
+ if (blockedReasons == BLOCKED_REASON_NONE) {
+ mUidBlockedReasons.delete(uid);
+ } else {
+ mUidBlockedReasons.put(uid, blockedReasons);
+ }
+ }
+
/**
* Notify of the blocked state apps with a registered callback matching a given NAI.
*
@@ -8234,7 +8252,10 @@
* any given nai, all requests need to be considered according to the uid who filed it.
*
* @param nai The target NetworkAgentInfo.
- * @param oldMetered True if the previous network capabilities is metered.
+ * @param oldMetered True if the previous network capabilities were metered.
+ * @param newMetered True if the current network capabilities are metered.
+ * @param oldBlockedUidRanges list of UID ranges previously blocked by lockdown VPN.
+ * @param newBlockedUidRanges list of UID ranges blocked by lockdown VPN.
*/
private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
boolean newMetered, List<UidRange> oldBlockedUidRanges,
@@ -8243,22 +8264,18 @@
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
- final boolean oldBlocked, newBlocked, oldVpnBlocked, newVpnBlocked;
- oldVpnBlocked = isUidBlockedByVpn(nri.mAsUid, oldBlockedUidRanges);
- newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges)
+ final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
+ final boolean oldVpnBlocked = isUidBlockedByVpn(nri.mAsUid, oldBlockedUidRanges);
+ final boolean newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges)
? isUidBlockedByVpn(nri.mAsUid, newBlockedUidRanges)
: oldVpnBlocked;
- final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
- oldBlocked = oldVpnBlocked || NetworkPolicyManager.isUidBlocked(
- blockedReasons, oldMetered);
- newBlocked = newVpnBlocked || NetworkPolicyManager.isUidBlocked(
- blockedReasons, newMetered);
-
- if (oldBlocked != newBlocked) {
+ final int oldBlockedState = getBlockedState(blockedReasons, oldMetered, oldVpnBlocked);
+ final int newBlockedState = getBlockedState(blockedReasons, newMetered, newVpnBlocked);
+ if (oldBlockedState != newBlockedState) {
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
- encodeBool(newBlocked));
+ newBlockedState);
}
}
}
@@ -8268,25 +8285,23 @@
* @param uid The uid for which the rules changed.
* @param blockedReasons The reasons for why an uid is blocked.
*/
- private void maybeNotifyNetworkBlockedForNewState(int uid, int blockedReasons) {
+ private void maybeNotifyNetworkBlockedForNewState(int uid, @BlockedReason int blockedReasons) {
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
final boolean metered = nai.networkCapabilities.isMetered();
final boolean vpnBlocked = isUidBlockedByVpn(uid, mVpnBlockedUidRanges);
- final boolean oldBlocked, newBlocked;
- oldBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked(
- mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered);
- newBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked(
- blockedReasons, metered);
- if (oldBlocked == newBlocked) {
+ final int oldBlockedState = getBlockedState(
+ mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered, vpnBlocked);
+ final int newBlockedState = getBlockedState(blockedReasons, metered, vpnBlocked);
+ if (oldBlockedState == newBlockedState) {
continue;
}
- final int arg = encodeBool(newBlocked);
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (nri != null && nri.mAsUid == uid) {
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg);
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
+ newBlockedState);
}
}
}
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index ebd32e8..08bff81 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -265,18 +265,9 @@
* Handler for on start pinning message
*/
private void handlePinOnStart() {
- final String bootImage = SystemProperties.get("dalvik.vm.boot-image", "");
- String[] filesToPin = null;
- if (bootImage.endsWith("boot-image.prof")) {
- // Use the files listed for that specific boot image.
- // TODO: find a better way to know we're using the JIT zygote configuration.
- filesToPin = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_jitzygoteBootImagePinnerServiceFiles);
- } else {
- // Files to pin come from the overlay and can be specified per-device config
- filesToPin = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_defaultPinnerServiceFiles);
- }
+ // Files to pin come from the overlay and can be specified per-device config
+ String[] filesToPin = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_defaultPinnerServiceFiles);
// Continue trying to pin each file even if we fail to pin some of them
for (String fileToPin : filesToPin) {
PinnedFile pf = pinFile(fileToPin,
@@ -286,10 +277,32 @@
Slog.e(TAG, "Failed to pin file = " + fileToPin);
continue;
}
-
synchronized (this) {
mPinnedFiles.add(pf);
}
+ if (fileToPin.endsWith(".jar") | fileToPin.endsWith(".apk")) {
+ // Check whether the runtime has compilation artifacts to pin.
+ String arch = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+ String[] files = null;
+ try {
+ files = DexFile.getDexFileOutputPaths(fileToPin, arch);
+ } catch (IOException ioe) { }
+ if (files == null) {
+ continue;
+ }
+ for (String file : files) {
+ PinnedFile df = pinFile(file,
+ Integer.MAX_VALUE,
+ /*attemptPinIntrospection=*/false);
+ if (df == null) {
+ Slog.i(TAG, "Failed to pin ART file = " + file);
+ continue;
+ }
+ synchronized (this) {
+ mPinnedFiles.add(df);
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 411fc67..b42a16d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -44,6 +44,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.telecom.TelecomManager;
import android.telephony.Annotation;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SrvccState;
@@ -215,6 +216,18 @@
return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
TelephonyCallback.PHONE_STATE_LISTENER_LIMIT_CHANGE_ID, uid));
}
+
+ /**
+ * See {@link TelecomManager#ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION} for more
+ * information.
+ * @noinspection ConstantConditions
+ */
+ public boolean isCallStateReadPhoneStateEnforcedInPlatformCompat(String packageName,
+ UserHandle userHandle) {
+ return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
+ TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION, packageName,
+ userHandle));
+ }
}
private final Context mContext;
@@ -366,11 +379,13 @@
|| events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
}
- private boolean isPhoneStatePermissionRequired(Set<Integer> events) {
+ private boolean isPhoneStatePermissionRequired(Set<Integer> events, int targetSdk) {
return events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
|| events.contains(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
|| events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
- || events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+ || events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)
+ || (targetSdk <= android.os.Build.VERSION_CODES.R ? events.contains(
+ TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED) : false);
}
private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) {
@@ -882,12 +897,12 @@
remove(callback.asBinder());
return;
}
-
+ int callerTargetSdk = TelephonyPermissions.getTargetSdk(mContext, callingPackage);
// Checks permission and throws SecurityException for disallowed operations. For pre-M
// apps whose runtime permission has been revoked, we return immediately to skip sending
// events to the app without crashing it.
if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId,
- "listen")) {
+ "listen", callerTargetSdk)) {
return;
}
@@ -920,7 +935,7 @@
}
r.phoneId = phoneId;
r.eventList = events;
- r.targetSdk = TelephonyPermissions.getTargetSdk(mContext, callingPackage);
+ r.targetSdk = callerTargetSdk;
if (DBG) {
log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
@@ -1180,7 +1195,9 @@
TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) {
try {
r.callback.onPhysicalChannelConfigChanged(
- mPhysicalChannelConfigs);
+ shouldSanitizeLocationForPhysicalChannelConfig(r)
+ ? getLocationSanitizedConfigs(mPhysicalChannelConfigs)
+ : mPhysicalChannelConfigs);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -2423,8 +2440,10 @@
return;
}
+ List<PhysicalChannelConfig> sanitizedConfigs = getLocationSanitizedConfigs(configs);
if (VDBG) {
- log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs);
+ log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs
+ + " sanitizedConfigs=" + sanitizedConfigs);
}
synchronized (mRecords) {
@@ -2437,11 +2456,14 @@
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
- log("notifyPhysicalChannelConfig: "
- + "mPhysicalChannelConfigs="
- + configs + " r=" + r);
+ log("notifyPhysicalChannelConfig: mPhysicalChannelConfigs="
+ + (shouldSanitizeLocationForPhysicalChannelConfig(r)
+ ? sanitizedConfigs : configs)
+ + " r=" + r);
}
- r.callback.onPhysicalChannelConfigChanged(configs);
+ r.callback.onPhysicalChannelConfigChanged(
+ shouldSanitizeLocationForPhysicalChannelConfig(r)
+ ? sanitizedConfigs : configs);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -2452,6 +2474,25 @@
}
}
+ private static boolean shouldSanitizeLocationForPhysicalChannelConfig(Record record) {
+ // Always redact location info from PhysicalChannelConfig if the registrant is from neither
+ // PHONE nor SYSTEM process. There is no user case that the registrant needs the location
+ // info (e.g. physicalCellId). This also remove the need for the location permissions check.
+ return record.callerUid != Process.PHONE_UID && record.callerUid != Process.SYSTEM_UID;
+ }
+
+ /**
+ * Return a copy of the PhysicalChannelConfig list but with location info removed.
+ */
+ private static List<PhysicalChannelConfig> getLocationSanitizedConfigs(
+ List<PhysicalChannelConfig> configs) {
+ List<PhysicalChannelConfig> sanitizedConfigs = new ArrayList<>(configs.size());
+ for (PhysicalChannelConfig config : configs) {
+ sanitizedConfigs.add(config.createLocationInfoSanitizedCopy());
+ }
+ return sanitizedConfigs;
+ }
+
/**
* Notify that the data enabled has changed.
*
@@ -2876,7 +2917,7 @@
}
private boolean checkListenerPermission(Set<Integer> events, int subId, String callingPackage,
- @Nullable String callingFeatureId, String message) {
+ @Nullable String callingFeatureId, String message, int targetSdk) {
LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
new LocationAccessPolicy.LocationPermissionQuery.Builder()
.setCallingPackage(callingPackage)
@@ -2912,13 +2953,26 @@
}
}
- if (isPhoneStatePermissionRequired(events)) {
+ if (isPhoneStatePermissionRequired(events, targetSdk)) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mContext, subId, callingPackage, callingFeatureId, message)) {
isPermissionCheckSuccessful = false;
}
}
+ // Only check READ_PHONE_STATE for CALL_STATE_CHANGED for API 31+.
+ if (mConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(callingPackage,
+ Binder.getCallingUserHandle())) {
+ if (events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, subId, callingPackage, callingFeatureId, message)) {
+ throw new SecurityException("CALL_STATE_CHANGED event requires "
+ + "READ_PHONE_STATE");
+ }
+ }
+ }
+
if (isPrecisePhoneStatePermissionRequired(events)) {
// check if calling app has either permission READ_PRECISE_PHONE_STATE
// or with carrier privileges
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index cfa110e..cd2d6d4 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -29,7 +29,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
@@ -158,6 +161,7 @@
@NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb;
@NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
@NonNull private final VcnContext mVcnContext;
+ @NonNull private final BroadcastReceiver mPkgChangeReceiver;
/** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */
@Nullable private LocationPermissionChecker mLocationPermissionChecker;
@@ -203,6 +207,29 @@
mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider);
+ mPkgChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+ || Intent.ACTION_PACKAGE_REPLACED.equals(action)
+ || Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ mTelephonySubscriptionTracker.handleSubscriptionsChanged();
+ } else {
+ Log.wtf(TAG, "received unexpected intent: " + action);
+ }
+ }
+ };
+
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ mContext.registerReceiver(
+ mPkgChangeReceiver, intentFilter, null /* broadcastPermission */, mHandler);
+
// Run on handler to ensure I/O does not block system server startup
mHandler.post(() -> {
PersistableBundle configBundle = null;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 98931a5..7f8d944 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -18,7 +18,6 @@
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
-import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
@@ -49,6 +48,7 @@
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerWhitelistManager.getReasonCodeFromProcState;
import static android.os.PowerWhitelistManager.reasonCodeToString;
+import static android.os.Process.INVALID_UID;
import static android.os.Process.NFC_UID;
import static android.os.Process.ROOT_UID;
import static android.os.Process.SHELL_UID;
@@ -660,7 +660,7 @@
}
ServiceRecord r = res.record;
- setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r,
+ setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, userId,
allowBackgroundActivityStarts);
if (!mAm.mUserController.exists(r.userId)) {
@@ -693,19 +693,7 @@
+ r.shortInstanceName;
Slog.w(TAG, msg);
showFgsBgRestrictedNotificationLocked(r);
- ApplicationInfo aInfo = null;
- try {
- aInfo = AppGlobals.getPackageManager().getApplicationInfo(
- callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
- userId);
- } catch (android.os.RemoteException e) {
- // pm is in same process, this will never happen.
- }
- if (aInfo == null) {
- throw new SecurityException("startServiceLocked failed, "
- + "could not resolve client package " + callingPackage);
- }
- if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
+ if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, callingUid)) {
throw new ForegroundServiceStartNotAllowedException(msg);
}
return null;
@@ -839,7 +827,7 @@
boolean addToStarting = false;
if (!callerFg && !fgRequired && r.app == null
&& mAm.mUserController.hasStartedUserState(r.userId)) {
- ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
+ ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid);
if (proc == null || proc.mState.getCurProcState() > PROCESS_STATE_RECEIVER) {
// If this is not coming from a foreground caller, then we may want
// to delay the start if there are already other background services
@@ -1808,7 +1796,7 @@
final long delayMs = SystemClock.elapsedRealtime() - r.createRealTime;
if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) {
setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
- r.appInfo.uid, r.intent.getIntent(), r, false);
+ r.appInfo.uid, r.intent.getIntent(), r, r.userId,false);
final String temp = "startForegroundDelayMs:" + delayMs;
if (r.mInfoAllowStartForeground != null) {
r.mInfoAllowStartForeground += "; " + temp;
@@ -1825,7 +1813,7 @@
r.mLastSetFgsRestrictionTime;
if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) {
setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
- r.appInfo.uid, r.intent.getIntent(), r, false);
+ r.appInfo.uid, r.intent.getIntent(), r, r.userId,false);
}
}
logFgsBackgroundStart(r);
@@ -1989,20 +1977,30 @@
&& isLegacyApp;
}
if (!showNow) {
- // is the notification such that it should show right away?
- showNow = r.foregroundNoti.shouldShowForegroundImmediately();
- // or is this an type of FGS that always shows immediately?
- if (!showNow) {
- switch (r.foregroundServiceType) {
- case ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK:
- case ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL:
- case ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE:
- case ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION:
- if (DEBUG_FOREGROUND_SERVICE) {
- Slog.d(TAG_SERVICE, "FGS " + r
- + " type gets immediate display");
- }
- showNow = true;
+ // has the app forced deferral?
+ if (!r.foregroundNoti.isForegroundDisplayForceDeferred()) {
+ // is the notification such that it should show right away?
+ showNow = r.foregroundNoti.shouldShowForegroundImmediately();
+ if (DEBUG_FOREGROUND_SERVICE && showNow) {
+ Slog.d(TAG_SERVICE, "FGS " + r
+ + " notification policy says show immediately");
+ }
+ // or is this an type of FGS that always shows immediately?
+ if (!showNow) {
+ switch (r.foregroundServiceType) {
+ case ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK:
+ case ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL:
+ case ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION:
+ if (DEBUG_FOREGROUND_SERVICE) {
+ Slog.d(TAG_SERVICE, "FGS " + r
+ + " type gets immediate display");
+ }
+ showNow = true;
+ }
+ }
+ } else {
+ if (DEBUG_FOREGROUND_SERVICE) {
+ Slog.d(TAG_SERVICE, "FGS " + r + " notification is app deferred");
}
}
}
@@ -2579,7 +2577,8 @@
return 0;
}
}
- setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, false);
+ setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
+ false);
if (s.app != null) {
ProcessServiceRecord servicePsr = s.app.mServices;
@@ -3451,7 +3450,7 @@
ProcessRecord app;
if (!isolated) {
- app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
+ app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+ " app=" + app);
if (app != null) {
@@ -3499,7 +3498,7 @@
// TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
// was initiated from a notification tap or not.
if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
- hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
+ hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated)) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
@@ -5469,7 +5468,7 @@
* @return true if allow, false otherwise.
*/
private void setFgsRestrictionLocked(String callingPackage,
- int callingPid, int callingUid, Intent intent, ServiceRecord r,
+ int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
boolean allowBackgroundActivityStarts) {
r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime();
// Check DeviceConfig flag.
@@ -5487,7 +5486,7 @@
if (r.mAllowStartForeground == REASON_DENIED) {
r.mAllowStartForeground = shouldAllowFgsStartForegroundLocked(allowWhileInUse,
callingPackage, callingPid, callingUid, intent, r,
- allowBackgroundActivityStarts);
+ userId);
}
}
}
@@ -5630,13 +5629,20 @@
*/
private @ReasonCode int shouldAllowFgsStartForegroundLocked(
@ReasonCode int allowWhileInUse, String callingPackage, int callingPid,
- int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
+ int callingUid, Intent intent, ServiceRecord r, int userId) {
FgsStartTempAllowList.TempFgsAllowListEntry tempAllowListReason =
r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid);
int ret = shouldAllowFgsStartForegroundLocked(allowWhileInUse, callingPid, callingUid,
callingPackage, r);
final int uidState = mAm.getUidStateLocked(callingUid);
+ int callerTargetSdkVersion = INVALID_UID;
+ try {
+ ApplicationInfo ai = mAm.mContext.getPackageManager().getApplicationInfoAsUser(
+ callingPackage, PackageManager.MATCH_KNOWN_PACKAGES, userId);
+ callerTargetSdkVersion = ai.targetSdkVersion;
+ } catch (PackageManager.NameNotFoundException e) {
+ }
final String debugInfo =
"[callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
@@ -5652,6 +5658,7 @@
+ ",callingUid:" + tempAllowListReason.mCallingUid))
+ ">"
+ "; targetSdkVersion:" + r.appInfo.targetSdkVersion
+ + "; callerTargetSdkVersion:" + callerTargetSdkVersion
+ "; startForegroundCount:" + r.mStartForegroundCount
+ "]";
if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
@@ -5823,7 +5830,12 @@
private boolean isBgFgsRestrictionEnabled(ServiceRecord r) {
return mAm.mConstants.mFlagFgsStartRestrictionEnabled
- && CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
+ // Checking service's targetSdkVersion.
+ && CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid)
+ && (!mAm.mConstants.mFgsStartRestrictionCheckCallerTargetSdk
+ // Checking callingUid's targetSdkVersion.
+ || CompatChanges.isChangeEnabled(
+ FGS_BG_START_RESTRICTION_CHANGE_ID, r.mRecentCallingUid));
}
private void logFgsBackgroundStart(ServiceRecord r) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index f7abf6a..e16f4c9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -172,6 +172,13 @@
"default_fgs_starts_restriction_enabled";
/**
+ * Default value for mFgsStartRestrictionCheckCallerTargetSdk if not explicitly set in
+ * Settings.Global.
+ */
+ private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK =
+ "default_fgs_starts_restriction_check_caller_target_sdk";
+
+ /**
* Whether FGS notification display is deferred following the transition into
* the foreground state. Default behavior is {@code true} unless overridden.
*/
@@ -371,6 +378,13 @@
// at all.
volatile boolean mFlagFgsStartRestrictionEnabled = true;
+ /**
+ * Indicates whether the foreground service background start restriction is enabled for
+ * caller app that is targeting S+.
+ * This is in addition to check of {@link #mFlagFgsStartRestrictionEnabled} flag.
+ */
+ volatile boolean mFgsStartRestrictionCheckCallerTargetSdk = true;
+
// Whether we defer FGS notifications a few seconds following their transition to
// the foreground state. Applies only to S+ apps; enabled by default.
volatile boolean mFlagFgsNotificationDeferralEnabled = true;
@@ -554,6 +568,9 @@
case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED:
updateFgsStartsRestriction();
break;
+ case KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK:
+ updateFgsStartsRestrictionCheckCallerTargetSdk();
+ break;
case KEY_DEFERRED_FGS_NOTIFICATIONS_ENABLED:
updateFgsNotificationDeferralEnable();
break;
@@ -829,6 +846,13 @@
/*defaultValue*/ true);
}
+ private void updateFgsStartsRestrictionCheckCallerTargetSdk() {
+ mFgsStartRestrictionCheckCallerTargetSdk = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK,
+ /*defaultValue*/ true);
+ }
+
private void updateFgsNotificationDeferralEnable() {
mFlagFgsNotificationDeferralEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1090,6 +1114,14 @@
pw.println(mFgToBgFgsGraceDuration);
pw.print(" "); pw.print(KEY_FGS_START_FOREGROUND_TIMEOUT); pw.print("=");
pw.println(mFgsStartForegroundTimeoutMs);
+ pw.print(" "); pw.print(KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED); pw.print("=");
+ pw.println(mFlagBackgroundActivityStartsEnabled);
+ pw.print(" "); pw.print(KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED);
+ pw.print("="); pw.println(mFlagBackgroundFgsStartRestrictionEnabled);
+ pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED); pw.print("=");
+ pw.println(mFlagFgsStartRestrictionEnabled);
+ pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK);
+ pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 321e3b1..2ee5c5a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -188,6 +188,7 @@
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
+import android.content.AttributionSource;
import android.content.AutofillOptions;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
@@ -213,7 +214,6 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionInfo;
import android.content.pm.ProcessInfo;
@@ -224,6 +224,7 @@
import android.content.pm.ServiceInfo;
import android.content.pm.TestUtilityService;
import android.content.pm.UserInfo;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -254,11 +255,11 @@
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.PowerExemptionManager.ReasonCode;
+import android.os.PowerExemptionManager.TempAllowListType;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
-import android.os.PowerWhitelistManager.ReasonCode;
-import android.os.PowerWhitelistManager.TempAllowListType;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -340,7 +341,11 @@
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintFunction;
+import com.android.internal.util.function.TriFunction;
import com.android.server.AlarmManagerInternal;
import com.android.server.DeviceIdleInternal;
import com.android.server.DisplayThread;
@@ -367,6 +372,7 @@
import com.android.server.job.JobSchedulerInternal;
import com.android.server.os.NativeTombstoneManager;
import com.android.server.pm.Installer;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.uri.GrantUri;
import com.android.server.uri.NeededUriGrants;
@@ -1123,24 +1129,6 @@
CoreSettingsObserver mCoreSettingsObserver;
/**
- * Thread-local storage used to carry caller permissions over through
- * indirect content-provider access.
- */
- private class Identity {
- public final IBinder token;
- public final int pid;
- public final int uid;
-
- Identity(IBinder _token, int _pid, int _uid) {
- token = _token;
- pid = _pid;
- uid = _uid;
- }
- }
-
- private static final ThreadLocal<Identity> sCallerIdentity = new ThreadLocal<Identity>();
-
- /**
* All information we have collected about the runtime performance of
* any user id that can impact battery performance.
*/
@@ -2604,8 +2592,8 @@
}
@GuardedBy("this")
- final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
- return mProcessList.getProcessRecordLocked(processName, uid, keepIfLarge);
+ final ProcessRecord getProcessRecordLocked(String processName, int uid) {
+ return mProcessList.getProcessRecordLocked(processName, uid);
}
@GuardedBy(anyOf = {"this", "mProcLock"})
@@ -2639,8 +2627,7 @@
false /* knownToBeDead */, 0 /* intentFlags */,
sNullHostingRecord /* hostingRecord */, ZYGOTE_POLICY_FLAG_EMPTY,
true /* allowWhileBooting */, true /* isolated */,
- uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
- crashHandler);
+ uid, abiOverride, entryPoint, entryPointArgs, crashHandler);
return proc != null;
}
}
@@ -2649,10 +2636,10 @@
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
- boolean isolated, boolean keepIfLarge) {
+ boolean isolated) {
return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
- keepIfLarge, null /* ABI override */, null /* entryPoint */,
+ null /* ABI override */, null /* entryPoint */,
null /* entryPointArgs */, null /* crashHandler */);
}
@@ -2971,9 +2958,9 @@
*/
@GuardedBy("this")
final void handleAppDiedLocked(ProcessRecord app, int pid,
- boolean restarting, boolean allowRestart) {
+ boolean restarting, boolean allowRestart, boolean fromBinderDied) {
boolean kept = cleanUpApplicationRecordLocked(app, pid, restarting, allowRestart, -1,
- false /*replacingPid*/);
+ false /*replacingPid*/, fromBinderDied);
if (!kept && !restarting) {
removeLruProcessLocked(app);
if (pid > 0) {
@@ -3031,12 +3018,15 @@
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
boolean fromBinderDied, String reason) {
// First check if this ProcessRecord is actually active for the pid.
+ final ProcessRecord curProc;
synchronized (mPidsSelfLocked) {
- ProcessRecord curProc = mPidsSelfLocked.get(pid);
- if (curProc != app) {
+ curProc = mPidsSelfLocked.get(pid);
+ }
+ if (curProc != app) {
+ if (!fromBinderDied || !mProcessList.handleDyingAppDeathLocked(app, pid)) {
Slog.w(TAG, "Spurious death for " + app + ", curProc for " + pid + ": " + curProc);
- return;
}
+ return;
}
mBatteryStatsService.noteProcessDied(app.info.uid, pid);
@@ -3076,7 +3066,7 @@
EventLogTags.writeAmProcDied(app.userId, pid, app.processName, setAdj, setProcState);
if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder());
- handleAppDiedLocked(app, pid, false, true);
+ handleAppDiedLocked(app, pid, false, true, fromBinderDied);
if (doOomAdj) {
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
@@ -3905,7 +3895,7 @@
// Only the system server can kill an application
if (callerUid == SYSTEM_UID) {
synchronized (this) {
- ProcessRecord app = getProcessRecordLocked(processName, uid, true);
+ ProcessRecord app = getProcessRecordLocked(processName, uid);
IApplicationThread thread;
if (app != null && (thread = app.getThread()) != null) {
try {
@@ -4233,7 +4223,7 @@
EventLog.writeEvent(0x534e4554, "131105245", app.getStartUid(), msg);
// If there is already an app occupying that pid that hasn't been cleaned up
cleanUpApplicationRecordLocked(app, pid, false, false, -1,
- true /*replacingPid*/);
+ true /*replacingPid*/, false /* fromBinderDied */);
removePidLocked(pid, app);
app = null;
}
@@ -4275,7 +4265,7 @@
// If this application record is still attached to a previous
// process, clean it up now.
if (app.getThread() != null) {
- handleAppDiedLocked(app, pid, true, true);
+ handleAppDiedLocked(app, pid, true, true, false /* fromBinderDied */);
}
// Tell the process all about itself.
@@ -4478,7 +4468,7 @@
app.unlinkDeathRecipient();
app.killLocked("error during bind", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
true);
- handleAppDiedLocked(app, pid, false, true);
+ handleAppDiedLocked(app, pid, false, true, false /* fromBinderDied */);
return false;
}
@@ -4543,7 +4533,7 @@
if (badApp) {
app.killLocked("error during init", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
true);
- handleAppDiedLocked(app, pid, false, true);
+ handleAppDiedLocked(app, pid, false, true, false /* fromBinderDied */);
return false;
}
@@ -5342,26 +5332,6 @@
return checkComponentPermission(permission, pid, uid, -1, true);
}
- @Override
- public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken) {
- if (permission == null) {
- return PackageManager.PERMISSION_DENIED;
- }
-
- // We might be performing an operation on behalf of an indirect binder
- // invocation, e.g. via {@link #openContentUri}. Check and adjust the
- // client identity accordingly before proceeding.
- Identity tlsIdentity = sCallerIdentity.get();
- if (tlsIdentity != null && tlsIdentity.token == callerToken) {
- Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
- + tlsIdentity.pid + "," + tlsIdentity.uid + "}");
- uid = tlsIdentity.uid;
- pid = tlsIdentity.pid;
- }
-
- return checkComponentPermission(permission, pid, uid, -1, true);
- }
-
/**
* Binder IPC calls go through the public entry point.
* This can be called with or without the global lock held.
@@ -5616,14 +5586,6 @@
final int modeFlags, int userId, IBinder callerToken) {
enforceNotIsolatedCaller("checkUriPermission");
- // Another redirected-binder-call permissions check as in
- // {@link checkPermissionWithToken}.
- Identity tlsIdentity = sCallerIdentity.get();
- if (tlsIdentity != null && tlsIdentity.token == callerToken) {
- uid = tlsIdentity.uid;
- pid = tlsIdentity.pid;
- }
-
// Our own process gets to do everything.
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
@@ -6101,7 +6063,7 @@
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
- info.uid, true);
+ info.uid);
} else {
app = null;
}
@@ -6157,23 +6119,22 @@
Binder.getCallingUid(), "*opencontent*", userId);
ParcelFileDescriptor pfd = null;
if (cph != null) {
- // We record the binder invoker's uid in thread-local storage before
- // going to the content provider to open the file. Later, in the code
- // that handles all permissions checks, we look for this uid and use
- // that rather than the Activity Manager's own uid. The effect is that
- // we do the check against the caller's permissions even though it looks
- // to the content provider like the Activity Manager itself is making
- // the request.
- Binder token = new Binder();
- sCallerIdentity.set(new Identity(
- token, Binder.getCallingPid(), Binder.getCallingUid()));
try {
- pfd = cph.provider.openFile(null, null, uri, "r", null, token);
+ // This method is exposed to the VNDK and to avoid changing its
+ // signature we just use the first package in the UID. For shared
+ // UIDs we may blame the wrong app but that is Okay as they are
+ // in the same security/privacy sandbox.
+ final AndroidPackage androidPackage = mPackageManagerInt
+ .getPackage(Binder.getCallingUid());
+ if (androidPackage == null) {
+ return null;
+ }
+ final AttributionSource attributionSource = new AttributionSource(
+ Binder.getCallingUid(), androidPackage.getPackageName(), null);
+ pfd = cph.provider.openFile(attributionSource, uri, "r", null);
} catch (FileNotFoundException e) {
// do nothing; pfd will be returned null
} finally {
- // Ensure that whatever happens, we clean up the identity state
- sCallerIdentity.remove();
// Ensure we're done with the provider.
mCpHelper.removeContentProviderExternalUnchecked(name, null, userId);
}
@@ -11501,7 +11462,8 @@
*/
@GuardedBy("this")
final boolean cleanUpApplicationRecordLocked(ProcessRecord app, int pid,
- boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
+ boolean restarting, boolean allowRestart, int index, boolean replacingPid,
+ boolean fromBinderDied) {
boolean restart;
synchronized (mProcLock) {
if (index >= 0) {
@@ -11509,7 +11471,10 @@
ProcessList.remove(pid);
}
- restart = app.onCleanupApplicationRecordLSP(mProcessStats, allowRestart);
+ // We don't want to unlinkDeathRecipient immediately, if it's not called from binder
+ // and it's not isolated, as we'd need the signal to bookkeeping the dying process list.
+ restart = app.onCleanupApplicationRecordLSP(mProcessStats, allowRestart,
+ fromBinderDied || app.isolated /* unlinkDeath */);
}
mAppProfiler.onCleanupApplicationRecordLocked(app);
skipCurrentReceiverLocked(app);
@@ -11539,25 +11504,7 @@
mProcessList.scheduleDispatchProcessDiedLocked(pid, app.info.uid);
// If this is a preceding instance of another process instance
- allowRestart = true;
- synchronized (app) {
- if (app.mSuccessor != null) {
- // We don't allow restart with this ProcessRecord now,
- // because we have created a new one already.
- allowRestart = false;
- // If it's persistent, add the successor to mPersistentStartingProcesses
- if (app.isPersistent() && !app.isRemoved()) {
- if (mPersistentStartingProcesses.indexOf(app.mSuccessor) < 0) {
- mPersistentStartingProcesses.add(app.mSuccessor);
- }
- }
- // clean up the field so the successor's proc starter could proceed.
- app.mSuccessor.mPredecessor = null;
- app.mSuccessor = null;
- // Notify if anyone is waiting for it.
- app.notifyAll();
- }
- }
+ allowRestart = mProcessList.handlePrecedingAppDiedLocked(app);
// If the caller is restarting this app, then leave it in its
// current lists and let the caller take care of it.
@@ -11933,7 +11880,7 @@
ProcessRecord proc = startProcessLocked(app.processName, app,
false, 0,
new HostingRecord("backup", hostingName),
- ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false, false);
+ ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false);
if (proc == null) {
Slog.e(TAG, "Unable to start backup agent process " + r);
return false;
@@ -13643,7 +13590,7 @@
ProcessRecord app;
synchronized (mProcLock) {
if (noRestart) {
- app = getProcessRecordLocked(ai.processName, ai.uid, true);
+ app = getProcessRecordLocked(ai.processName, ai.uid);
} else {
// Instrumentation can kill and relaunch even persistent processes
forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false,
@@ -13686,7 +13633,7 @@
ApplicationInfo targetInfo) {
ProcessRecord pr;
synchronized (this) {
- pr = getProcessRecordLocked(targetInfo.processName, targetInfo.uid, true);
+ pr = getProcessRecordLocked(targetInfo.processName, targetInfo.uid);
}
try {
@@ -14653,7 +14600,8 @@
}
}
didSomething = true;
- cleanUpApplicationRecordLocked(app, pid, false, true, -1, false /*replacingPid*/);
+ cleanUpApplicationRecordLocked(app, pid, false, true, -1, false /*replacingPid*/,
+ false /* fromBinderDied */);
mProcessList.mRemovedProcesses.remove(i);
if (app.isPersistent()) {
@@ -15433,8 +15381,7 @@
@Override
public void killProcess(String processName, int uid, String reason) {
synchronized (ActivityManagerService.this) {
- final ProcessRecord proc = getProcessRecordLocked(processName, uid,
- true /* keepIfLarge */);
+ final ProcessRecord proc = getProcessRecordLocked(processName, uid);
if (proc != null) {
mProcessList.removeProcessLocked(proc, false /* callerWillRestart */,
true /* allowRestart */, ApplicationExitInfo.REASON_OTHER, reason);
@@ -15841,7 +15788,7 @@
startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
new HostingRecord(hostingType, hostingName, isTop),
ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
- false /* isolated */, true /* keepIfLarge */);
+ false /* isolated */);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -16301,7 +16248,7 @@
private void updateApplicationInfoLOSP(@NonNull List<String> packagesToUpdate, int userId) {
final boolean updateFrameworkRes = packagesToUpdate.contains("android");
if (updateFrameworkRes) {
- PackageParser.readConfigUseRoundIcon(null);
+ ParsingPackageUtils.readConfigUseRoundIcon(null);
}
mProcessList.updateApplicationInfoLOSP(packagesToUpdate, userId, updateFrameworkRes);
@@ -16651,6 +16598,74 @@
message, shouldCollectMessage);
}
+ @Override
+ public int noteProxyOperation(int code, @NonNull AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, boolean skiProxyOperation,
+ @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean,
+ Boolean, Integer> superImpl) {
+ if (attributionSource.getUid() == mTargetUid && isTargetOp(code)) {
+ final int shellUid = UserHandle.getUid(UserHandle.getUserId(
+ attributionSource.getUid()), Process.SHELL_UID);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(code, new AttributionSource(shellUid,
+ "com.android.shell", attributionSource.getAttributionTag(),
+ attributionSource.getNext()),
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skiProxyOperation);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
+ message, shouldCollectMessage, skiProxyOperation);
+ }
+
+ @Override
+ public int startProxyOperation(IBinder token, int code,
+ @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProsyOperation, @NonNull OctFunction<IBinder, Integer,
+ AttributionSource, Boolean, Boolean, String, Boolean, Boolean,
+ Integer> superImpl) {
+ if (attributionSource.getUid() == mTargetUid && isTargetOp(code)) {
+ final int shellUid = UserHandle.getUid(UserHandle.getUserId(
+ attributionSource.getUid()), Process.SHELL_UID);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(token, code, new AttributionSource(shellUid,
+ "com.android.shell", attributionSource.getAttributionTag(),
+ attributionSource.getNext()), startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProsyOperation);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return superImpl.apply(token, code, attributionSource, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProsyOperation);
+ }
+
+ @Override
+ public void finishProxyOperation(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource,
+ @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl) {
+ if (attributionSource.getUid() == mTargetUid && isTargetOp(code)) {
+ final int shellUid = UserHandle.getUid(UserHandle.getUserId(
+ attributionSource.getUid()), Process.SHELL_UID);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ superImpl.apply(clientId, code, new AttributionSource(shellUid,
+ "com.android.shell", attributionSource.getAttributionTag(),
+ attributionSource.getNext()));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ superImpl.apply(clientId, code, attributionSource);
+ }
+
private boolean isTargetOp(int code) {
// null permissions means all ops are targeted
if (mPermissions == null) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index e79f096..7f2eae8 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -45,8 +45,8 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerWhitelistManager;
-import android.os.PowerWhitelistManager.TempAllowListType;
+import android.os.PowerExemptionManager.ReasonCode;
+import android.os.PowerExemptionManager.TempAllowListType;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -908,7 +908,7 @@
}
final void scheduleTempAllowlistLocked(int uid, long duration, BroadcastRecord r,
- @TempAllowListType int type, @PowerWhitelistManager.ReasonCode int reasonCode,
+ @TempAllowListType int type, @ReasonCode int reasonCode,
@Nullable String reason) {
if (duration > Integer.MAX_VALUE) {
duration = Integer.MAX_VALUE;
@@ -1544,7 +1544,7 @@
}
String targetProcess = info.activityInfo.processName;
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
- info.activityInfo.applicationInfo.uid, false);
+ info.activityInfo.applicationInfo.uid);
if (!skip) {
final int allowed = mService.getAppStartModeLOSP(
@@ -1678,7 +1678,7 @@
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
new HostingRecord("broadcast", r.curComponent), isActivityCapable
? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
- (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false);
+ (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
if (r.curApp == null) {
// Ah, this recipient is unavailable. Finish it if necessary,
// and mark the broadcast record as ready for the next.
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 2c8794d..b44699b 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -33,6 +33,7 @@
import android.app.ContentProviderHolder;
import android.app.IApplicationThread;
import android.app.usage.UsageEvents.Event;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -42,6 +43,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.database.ContentObserver;
@@ -67,7 +69,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import com.android.server.RescueParty;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -436,7 +440,7 @@
// Use existing process if already started
checkTime(startTime, "getContentProviderImpl: looking for process record");
ProcessRecord proc = mService.getProcessRecordLocked(
- cpi.processName, cpr.appInfo.uid, false);
+ cpi.processName, cpr.appInfo.uid);
IApplicationThread thread;
if (proc != null && (thread = proc.getThread()) != null
&& !proc.isKilled()) {
@@ -459,7 +463,7 @@
new HostingRecord("content provider",
new ComponentName(
cpi.applicationInfo.packageName, cpi.name)),
- Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
+ Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
@@ -1037,7 +1041,19 @@
holder = getContentProviderExternalUnchecked(name, null, callingUid,
"*checkContentProviderUriPermission*", userId);
if (holder != null) {
- return holder.provider.checkUriPermission(null, null, uri, callingUid, modeFlags);
+
+ final PackageManagerInternal packageManagerInt = LocalServices.getService(
+ PackageManagerInternal.class);
+ final AndroidPackage androidPackage = packageManagerInt
+ .getPackage(Binder.getCallingUid());
+ if (androidPackage == null) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ final AttributionSource attributionSource = new AttributionSource(
+ callingUid, androidPackage.getPackageName(), null);
+ return holder.provider.checkUriPermission(attributionSource, uri, callingUid,
+ modeFlags);
}
} catch (RemoteException e) {
Log.w(TAG, "Content provider dead retrieving " + uri, e);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 5ae65ef..8ad11d1 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1997,7 +1997,8 @@
// in this case unless they explicitly request it.
if ((cstate.getCurCapability() & PROCESS_CAPABILITY_NETWORK) != 0) {
if (clientProcState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
- if ((cr.flags & Context.BIND_ALLOW_NETWORK_ACCESS) != 0) {
+ if ((cr.flags & Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS)
+ != 0) {
capability |= PROCESS_CAPABILITY_NETWORK;
}
} else {
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 51666ac..f7c777e 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -66,7 +66,7 @@
/**
* Map IBinder to duration specified as Pair<Long, Integer>, Long is allowlist duration in
* milliseconds, Integer is allowlist type defined at
- * {@link android.os.PowerWhitelistManager.TempAllowListType}
+ * {@link android.os.PowerExemptionManager.TempAllowListType}
*/
private ArrayMap<IBinder, TempAllowListDuration> mAllowlistDuration;
private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 51bcde8..fae941d 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -36,7 +36,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -54,7 +53,6 @@
import static com.android.server.am.ActivityManagerService.TAG_NETWORK;
import static com.android.server.am.ActivityManagerService.TAG_PROCESSES;
import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
-import static com.android.server.am.AppProfiler.TAG_PSS;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -687,6 +685,12 @@
@GuardedBy("mService")
final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
+ /**
+ * Processes that are killed by us and being waiting for the death notification.
+ */
+ @GuardedBy("mService")
+ final ProcessMap<ProcessRecord> mDyingProcesses = new ProcessMap<>();
+
// Self locked with the inner lock within the RemoteCallbackList
private final RemoteCallbackList<IProcessObserver> mProcessObservers =
new RemoteCallbackList<>();
@@ -1501,8 +1505,7 @@
}
@GuardedBy("mService")
- final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean
- keepIfLarge) {
+ ProcessRecord getProcessRecordLocked(String processName, int uid) {
if (uid == SYSTEM_UID) {
// The system gets to run in any process. If there are multiple
// processes with the same uid, just pick the first (this
@@ -1519,41 +1522,7 @@
return procs.valueAt(i);
}
}
- ProcessRecord proc = mProcessNames.get(processName, uid);
- if (false && proc != null && !keepIfLarge
- && proc.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
- && proc.mProfile.getLastCachedPss() >= 4000) {
- // Turn this condition on to cause killing to happen regularly, for testing.
- synchronized (mService.mAppProfiler.mProfilerLock) {
- proc.mProfile.reportCachedKill();
- }
- proc.killLocked(Long.toString(proc.mProfile.getLastCachedPss()) + "k from cached",
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_LARGE_CACHED,
- true);
- } else if (proc != null && !keepIfLarge
- && !mService.mAppProfiler.isLastMemoryLevelNormal()
- && proc.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
- final long lastCachedPss;
- boolean doKilling = false;
- synchronized (mService.mAppProfiler.mProfilerLock) {
- lastCachedPss = proc.mProfile.getLastCachedPss();
- if (lastCachedPss >= getCachedRestoreThresholdKb()) {
- proc.mProfile.reportCachedKill();
- doKilling = true;
- }
- }
- if (DEBUG_PSS) {
- Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + lastCachedPss);
- }
- if (doKilling) {
- proc.killLocked(Long.toString(lastCachedPss) + "k from cached",
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_LARGE_CACHED,
- true);
- }
- }
- return proc;
+ return mProcessNames.get(processName, uid);
}
void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
@@ -1756,12 +1725,13 @@
private boolean enableNativeHeapZeroInit(ProcessRecord app) {
// Look at the process attribute first.
- if (app.processInfo != null && app.processInfo.nativeHeapZeroInit != null) {
- return app.processInfo.nativeHeapZeroInit;
+ if (app.processInfo != null
+ && app.processInfo.nativeHeapZeroInitialized != ApplicationInfo.ZEROINIT_DEFAULT) {
+ return app.processInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_ENABLED;
}
// Then at the application attribute.
- if (app.info.isNativeHeapZeroInit() != null) {
- return app.info.isNativeHeapZeroInit();
+ if (app.info.getNativeHeapZeroInitialized() != ApplicationInfo.ZEROINIT_DEFAULT) {
+ return app.info.getNativeHeapZeroInitialized() == ApplicationInfo.ZEROINIT_ENABLED;
}
// Compat feature last.
if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_ZERO_INIT, app.info)) {
@@ -1790,6 +1760,9 @@
app.setPid(0);
app.setStartSeq(0);
}
+ // Clear any residual death recipient link as the ProcessRecord could be reused.
+ app.unlinkDeathRecipient();
+ app.setDyingPid(0);
if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(
TAG_PROCESSES,
@@ -2086,14 +2059,14 @@
// If there is a preceding instance of the process, wait for its death with a timeout.
// Use local reference since we are not using locks here
final ProcessRecord predecessor = app.mPredecessor;
- if (predecessor != null) {
- final int pid = predecessor.getPid();
+ int prevPid;
+ if (predecessor != null && (prevPid = predecessor.getDyingPid()) > 0) {
long now = System.currentTimeMillis();
final long end = now + PROC_KILL_TIMEOUT;
final int oldPolicy = StrictMode.getThreadPolicyMask();
try {
StrictMode.setThreadPolicyMask(0);
- Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
+ Process.waitForProcessDeath(prevPid, PROC_KILL_TIMEOUT);
// It's killed successfully, but we'd make sure the cleanup work is done.
synchronized (predecessor) {
if (app.mPredecessor != null) {
@@ -2103,17 +2076,22 @@
predecessor.wait(end - now);
} catch (InterruptedException e) {
}
+ if (System.currentTimeMillis() >= end) {
+ Slog.w(TAG, predecessor + " " + prevPid
+ + " has died but its obituary delivery is slow.");
+ }
}
}
- if (app.mPredecessor != null) {
+ if (app.mPredecessor != null && app.mPredecessor.getPid() > 0) {
// The cleanup work hasn't be done yet, let's log it and continue.
- Slog.w(TAG, predecessor + " has died, but its cleanup isn't done");
+ Slog.w(TAG, predecessor + " " + prevPid
+ + " has died, but its cleanup isn't done");
}
}
} catch (Exception e) {
// It's still alive... maybe blocked at uninterruptible sleep ?
- Slog.wtf(TAG, predecessor.toString() + " refused to die, but we need to launch "
- + app, e);
+ Slog.wtf(TAG, predecessor.toString() + " " + prevPid
+ + " refused to die, but we need to launch " + app, e);
} finally {
StrictMode.setThreadPolicyMask(oldPolicy);
}
@@ -2408,12 +2386,11 @@
ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
- boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs,
- Runnable crashHandler) {
+ String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
long startTime = SystemClock.uptimeMillis();
ProcessRecord app;
if (!isolated) {
- app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
+ app = getProcessRecordLocked(processName, info.uid);
checkSlow(startTime, "startProcess: after getProcessRecord");
if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
@@ -2476,12 +2453,8 @@
ProcessList.killProcessGroup(app.uid, app.getPid());
checkSlow(startTime, "startProcess: done killing old proc");
- if (!app.isKilled()
- || mService.mAppProfiler.isLastMemoryLevelNormal()
- || app.mState.getSetProcState() < ActivityManager.PROCESS_STATE_CACHED_EMPTY
- || app.mProfile.getLastCachedPss() < getCachedRestoreThresholdKb()) {
- // Throw a wtf if it's not killed, or killed but not because the system was in
- // memory pressure + the app was in "cch-empty" and used large amount of memory
+ if (!app.isKilled()) {
+ // Throw a wtf if it's not killed
Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
} else {
Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
@@ -2490,6 +2463,18 @@
// routine of it yet, but we'd set it as the predecessor of the new process.
predecessor = app;
app = null;
+ } else if (!isolated) {
+ // This app may have been removed from process name maps, probably because we killed it
+ // and did the cleanup before the actual death notification. Check the dying processes.
+ predecessor = mDyingProcesses.get(processName, info.uid);
+ if (predecessor != null) {
+ if (app != null) {
+ app.mPredecessor = predecessor;
+ predecessor.mSuccessor = app;
+ }
+ Slog.w(TAG_PROCESSES, predecessor.toString() + " is attached to a previous process "
+ + predecessor.getDyingPid());
+ }
}
if (app == null) {
@@ -2658,7 +2643,7 @@
+ " belongs to another existing app:" + oldApp.processName
+ " startSeq:" + oldApp.getStartSeq());
mService.cleanUpApplicationRecordLocked(oldApp, pid, false, false, -1,
- true /*replacingPid*/);
+ true /*replacingPid*/, false /* fromBinderDied */);
}
mService.addPidLocked(app);
synchronized (mService.mPidsSelfLocked) {
@@ -2872,7 +2857,8 @@
}
}
app.killLocked(reason, reasonCode, subReason, true);
- mService.handleAppDiedLocked(app, pid, willRestart, allowRestart);
+ mService.handleAppDiedLocked(app, pid, willRestart, allowRestart,
+ false /* fromBinderDied */);
if (willRestart) {
removeLruProcessLocked(app);
mService.addAppLocked(app.info, null, false, null /* ABI override */,
@@ -4903,6 +4889,55 @@
}
/**
+ * Handle the death notification if it's a dying app.
+ *
+ * @return {@code true} if it's a dying app that we were tracking.
+ */
+ @GuardedBy("mService")
+ boolean handleDyingAppDeathLocked(ProcessRecord app, int pid) {
+ if (mProcessNames.get(app.processName, app.uid) != app
+ && mDyingProcesses.get(app.processName, app.uid) == app) {
+ // App has been removed already, meaning cleanup has done.
+ Slog.v(TAG, "Got obituary of " + pid + ":" + app.processName);
+ app.unlinkDeathRecipient();
+ handlePrecedingAppDiedLocked(app);
+ // It's really gone now, let's remove from the dying process list.
+ mDyingProcesses.remove(app.processName, app.uid);
+ app.setDyingPid(0);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handle the case where the given app is a preceding instance of another process instance.
+ *
+ * @return {@code false} if this given app should not be allowed to restart.
+ */
+ @GuardedBy("mService")
+ boolean handlePrecedingAppDiedLocked(ProcessRecord app) {
+ synchronized (app) {
+ if (app.mSuccessor != null) {
+ // We don't allow restart with this ProcessRecord now,
+ // because we have created a new one already.
+ // If it's persistent, add the successor to mPersistentStartingProcesses
+ if (app.isPersistent() && !app.isRemoved()) {
+ if (mService.mPersistentStartingProcesses.indexOf(app.mSuccessor) < 0) {
+ mService.mPersistentStartingProcesses.add(app.mSuccessor);
+ }
+ }
+ // clean up the field so the successor's proc starter could proceed.
+ app.mSuccessor.mPredecessor = null;
+ app.mSuccessor = null;
+ // Notify if anyone is waiting for it.
+ app.notifyAll();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Called by ActivityManagerService when a process died.
*/
@GuardedBy("mService")
@@ -4912,21 +4947,33 @@
}
Watchdog.getInstance().processDied(app.processName, app.getPid());
+ if (app.getDeathRecipient() == null) {
+ // If we've done unlinkDeathRecipient before calling into this, remove from dying list.
+ mDyingProcesses.remove(app.processName, app.uid);
+ app.setDyingPid(0);
+ }
mAppExitInfoTracker.scheduleNoteProcessDied(app);
}
/**
* Called by ActivityManagerService when it decides to kill an application process.
*/
+ @GuardedBy("mService")
void noteAppKill(final ProcessRecord app, final @Reason int reason,
final @SubReason int subReason, final String msg) {
if (DEBUG_PROCESSES) {
Slog.i(TAG, "note: " + app + " is being killed, reason: " + reason
+ ", sub-reason: " + subReason + ", message: " + msg);
}
+ if (app.getPid() > 0 && !app.isolated && app.getDeathRecipient() != null) {
+ // We are killing it, put it into the dying process list.
+ mDyingProcesses.put(app.processName, app.uid, app);
+ app.setDyingPid(app.getPid());
+ }
mAppExitInfoTracker.scheduleNoteAppKill(app, reason, subReason, msg);
}
+ @GuardedBy("mService")
void noteAppKill(final int pid, final int uid, final @Reason int reason,
final @SubReason int subReason, final String msg) {
if (DEBUG_PROCESSES) {
@@ -4934,6 +4981,15 @@
+ ", sub-reason: " + subReason + ", message: " + msg);
}
+ final ProcessRecord app;
+ synchronized (mService.mPidsSelfLocked) {
+ app = mService.mPidsSelfLocked.get(pid);
+ }
+ if (app != null && app.uid == uid && !app.isolated && app.getDeathRecipient() != null) {
+ // We are killing it, put it into the dying process list.
+ mDyingProcesses.put(app.processName, uid, app);
+ app.setDyingPid(app.getPid());
+ }
mAppExitInfoTracker.scheduleNoteAppKill(pid, uid, reason, subReason, msg);
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 42e7ff4..ed136af 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -107,6 +107,12 @@
int mPid;
/**
+ * The process ID which will be set when we're killing this process.
+ */
+ @GuardedBy("mService")
+ private int mDyingPid;
+
+ /**
* The gids this process was launched with.
*/
@GuardedBy("mService")
@@ -479,7 +485,7 @@
if (procInfo != null && procInfo.deniedPermissions == null
&& procInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_DEFAULT
&& procInfo.memtagMode == ApplicationInfo.MEMTAG_DEFAULT
- && procInfo.nativeHeapZeroInit == null) {
+ && procInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_DEFAULT) {
// If this process hasn't asked for permissions to be denied, or for a
// non-default GwpAsan mode, or any other non-default setting, then we don't
// care about it.
@@ -572,6 +578,16 @@
}
@GuardedBy("mService")
+ int getDyingPid() {
+ return mDyingPid;
+ }
+
+ @GuardedBy("mService")
+ void setDyingPid(int dyingPid) {
+ mDyingPid = dyingPid;
+ }
+
+ @GuardedBy("mService")
int[] getGids() {
return mGids;
}
@@ -732,6 +748,11 @@
mDeathRecipient = deathRecipient;
}
+ @GuardedBy("mService")
+ IBinder.DeathRecipient getDeathRecipient() {
+ return mDeathRecipient;
+ }
+
@GuardedBy({"mService", "mProcLock"})
void setActiveInstrumentation(ActiveInstrumentation instr) {
mInstr = instr;
@@ -896,11 +917,14 @@
}
@GuardedBy({"mService", "mProcLock"})
- boolean onCleanupApplicationRecordLSP(ProcessStatsService processStats, boolean allowRestart) {
+ boolean onCleanupApplicationRecordLSP(ProcessStatsService processStats, boolean allowRestart,
+ boolean unlinkDeath) {
mErrorState.onCleanupApplicationRecordLSP();
resetPackageList(processStats);
- unlinkDeathRecipient();
+ if (unlinkDeath) {
+ unlinkDeathRecipient();
+ }
makeInactive(processStats);
setWaitingToKill(null);
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 2ee41ac..57de708 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -16,6 +16,17 @@
package com.android.server.app;
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+
+import static com.android.server.wm.CompatModePackages.DOWNSCALED;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -23,27 +34,41 @@
import android.app.GameManager;
import android.app.GameManager.GameMode;
import android.app.IGameManagerService;
+import android.compat.Compatibility;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ShellCallback;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
import android.util.ArrayMap;
-import android.util.Log;
+import android.util.KeyValueListParser;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.CompatibilityChangeConfig;
+import com.android.internal.compat.IPlatformCompat;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import java.io.FileDescriptor;
+import java.util.HashSet;
+import java.util.List;
/**
* Service to manage game related features.
@@ -60,14 +85,20 @@
static final int WRITE_SETTINGS = 1;
static final int REMOVE_SETTINGS = 2;
+ static final int POPULATE_GAME_MODE_SETTINGS = 3;
static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds
private final Context mContext;
- private final PackageManager mPackageManager;
private final Object mLock = new Object();
+ private final Object mDeviceConfigLock = new Object();
private final Handler mHandler;
+ private final PackageManager mPackageManager;
+ private final IPlatformCompat mPlatformCompat;
+ private DeviceConfigListener mDeviceConfigListener;
@GuardedBy("mLock")
private final ArrayMap<Integer, GameManagerSettings> mSettings = new ArrayMap<>();
+ @GuardedBy("mDeviceConfigLock")
+ private final ArrayMap<String, GamePackageConfiguration> mConfigs = new ArrayMap<>();
public GameManagerService(Context context) {
this(context, createServiceThread().getLooper());
@@ -76,7 +107,9 @@
GameManagerService(Context context, Looper looper) {
mContext = context;
mHandler = new SettingsHandler(looper);
- mPackageManager = context.getPackageManager();
+ mPackageManager = mContext.getPackageManager();
+ mPlatformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
}
@Override
@@ -143,10 +176,186 @@
}
break;
}
+ case POPULATE_GAME_MODE_SETTINGS: {
+ removeMessages(POPULATE_GAME_MODE_SETTINGS, msg.obj);
+ loadDeviceConfigLocked();
+ break;
+ }
}
}
}
+ private class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
+
+ DeviceConfigListener() {
+ super();
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_GAME_OVERLAY,
+ mContext.getMainExecutor(), this);
+ }
+
+ @Override
+ public void onPropertiesChanged(Properties properties) {
+ synchronized (mDeviceConfigLock) {
+ for (String key : properties.getKeyset()) {
+ try {
+ // Check if the package is installed before caching it.
+ final String packageName = keyToPackageName(key);
+ mPackageManager.getPackageInfo(packageName, 0);
+ final GamePackageConfiguration config =
+ GamePackageConfiguration.fromProperties(key, properties);
+ if (config.isValid()) {
+ putConfig(config);
+ } else {
+ // This means that we received a bad config, or the config was deleted.
+ Slog.i(TAG, "Removing config for: " + packageName);
+ mConfigs.remove(packageName);
+ disableCompatScale(packageName);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ if (DEBUG) {
+ Slog.v(TAG, "Package name not found", e);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void finalize() {
+ DeviceConfig.removeOnPropertiesChangedListener(this);
+ }
+ }
+
+ private static class GameModeConfiguration {
+ public static final String TAG = "GameManagerService_GameModeConfiguration";
+ public static final String MODE_KEY = "mode";
+ public static final String SCALING_KEY = "downscaleFactor";
+
+ private final @GameMode int mGameMode;
+ private final String mScaling;
+
+ private GameModeConfiguration(@NonNull int gameMode,
+ @NonNull String scaling) {
+ mGameMode = gameMode;
+ mScaling = scaling;
+ }
+
+ public static GameModeConfiguration fromKeyValueListParser(KeyValueListParser parser) {
+ return new GameModeConfiguration(
+ parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED),
+ parser.getString(SCALING_KEY, "1.0")
+ );
+ }
+
+ public int getGameMode() {
+ return mGameMode;
+ }
+
+ public String getScaling() {
+ return mScaling;
+ }
+
+ public boolean isValid() {
+ return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
+ || mGameMode == GameManager.GAME_MODE_BATTERY) && getCompatChangeId() != 0;
+ }
+
+ public String toString() {
+ return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]";
+ }
+
+ public long getCompatChangeId() {
+ switch (mScaling) {
+ case "0.5":
+ return DOWNSCALE_50;
+ case "0.6":
+ return DOWNSCALE_60;
+ case "0.7":
+ return DOWNSCALE_70;
+ case "0.8":
+ return DOWNSCALE_80;
+ case "0.9":
+ return DOWNSCALE_90;
+ }
+ return 0;
+ }
+ }
+
+ private static class GamePackageConfiguration {
+ public static final String TAG = "GameManagerService_GamePackageConfiguration";
+
+ private final String mPackageName;
+ private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
+
+ private GamePackageConfiguration(String keyName) {
+ mPackageName = keyToPackageName(keyName);
+ mModeConfigs = new ArrayMap<>();
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public @GameMode int[] getAvailableGameModes() {
+ if (mModeConfigs.keySet().size() > 0) {
+ return mModeConfigs.keySet().stream()
+ .mapToInt(Integer::intValue).toArray();
+ }
+ return new int[]{GameManager.GAME_MODE_UNSUPPORTED};
+ }
+
+ /**
+ * Get a GameModeConfiguration for a given game mode.
+ *
+ * @return The package's GameModeConfiguration for the provided mode or null if absent
+ */
+ public GameModeConfiguration getGameModeConfiguration(@GameMode int gameMode) {
+ return mModeConfigs.get(gameMode);
+ }
+
+ /**
+ * Insert a new GameModeConfiguration
+ */
+ public void addModeConfig(GameModeConfiguration config) {
+ if (config.isValid()) {
+ mModeConfigs.put(config.getGameMode(), config);
+ } else {
+ Slog.w(TAG, "Invalid game mode config for "
+ + mPackageName + ":" + config.toString());
+ }
+ }
+
+ /**
+ * Create a new instance from a package name and DeviceConfig.Properties instance
+ */
+ public static GamePackageConfiguration fromProperties(String key,
+ Properties properties) {
+ final GamePackageConfiguration packageConfig = new GamePackageConfiguration(key);
+ final String configString = properties.getString(key, "");
+ final String[] gameModeConfigStrings = configString.split(":");
+ for (String gameModeConfigString : gameModeConfigStrings) {
+ try {
+ final KeyValueListParser parser = new KeyValueListParser(',');
+ parser.setString(gameModeConfigString);
+ final GameModeConfiguration config =
+ GameModeConfiguration.fromKeyValueListParser(parser);
+ packageConfig.addModeConfig(config);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Invalid config string");
+ }
+ }
+ return packageConfig;
+ }
+
+ public boolean isValid() {
+ return mModeConfigs.size() > 0;
+ }
+
+ public String toString() {
+ return "[Name:" + mPackageName + " Modes: " + mModeConfigs.toString() + "]";
+ }
+ }
+
/**
* SystemService lifecycle for GameService.
*
@@ -163,6 +372,8 @@
public void onStart() {
mService = new GameManagerService(getContext());
publishBinderService(Context.GAME_SERVICE, mService);
+ mService.registerDeviceConfigListener();
+ mService.registerPackageReceiver();
}
@Override
@@ -200,10 +411,27 @@
}
}
+ /**
+ * Get an array of game modes available for a given package.
+ * Checks that the caller has {@link android.Manifest.permission#MANAGE_GAME_MODE}.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public @GameMode int[] getAvailableGameModes(String packageName) throws SecurityException {
+ checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+ synchronized (mDeviceConfigLock) {
+ final GamePackageConfiguration config = mConfigs.get(packageName);
+ if (config == null) {
+ return new int[]{GameManager.GAME_MODE_UNSUPPORTED};
+ }
+ return config.getAvailableGameModes();
+ }
+ }
+
private @GameMode int getGameModeFromSettings(String packageName, int userId) {
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
- Log.w(TAG, "User ID '" + userId + "' does not have a Game Mode"
+ Slog.w(TAG, "User ID '" + userId + "' does not have a Game Mode"
+ " selected for package: '" + packageName + "'");
return GameManager.GAME_MODE_UNSUPPORTED;
}
@@ -229,7 +457,7 @@
final ApplicationInfo applicationInfo = mPackageManager
.getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
- Log.e(TAG, "Ignoring attempt to get the Game Mode for '" + packageName
+ Slog.e(TAG, "Ignoring attempt to get the Game Mode for '" + packageName
+ "' which is not categorized as a game: applicationInfo.flags = "
+ applicationInfo.flags + ", category = " + applicationInfo.category);
return GameManager.GAME_MODE_UNSUPPORTED;
@@ -269,7 +497,7 @@
final ApplicationInfo applicationInfo = mPackageManager
.getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
- Log.e(TAG, "Ignoring attempt to set the Game Mode for '" + packageName
+ Slog.e(TAG, "Ignoring attempt to set the Game Mode for '" + packageName
+ "' which is not categorized as a game: applicationInfo.flags = "
+ applicationInfo.flags + ", category = " + applicationInfo.category);
return;
@@ -295,6 +523,7 @@
mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
}
}
+ updateCompatModeDownscale(packageName, gameMode);
}
/**
@@ -303,6 +532,8 @@
@VisibleForTesting
void onBootCompleted() {
Slog.d(TAG, "onBootCompleted");
+ final Message msg = mHandler.obtainMessage(POPULATE_GAME_MODE_SETTINGS);
+ mHandler.sendMessage(msg);
}
void onUserStarting(int userId) {
@@ -329,6 +560,187 @@
}
}
+ private void loadDeviceConfigLocked() {
+ final List<PackageInfo> packages = mPackageManager.getInstalledPackages(0);
+ final String[] packageNames = packages.stream().map(e -> packageNameToKey(e.packageName))
+ .toArray(String[]::new);
+ synchronized (mDeviceConfigLock) {
+ final Properties properties = DeviceConfig.getProperties(
+ DeviceConfig.NAMESPACE_GAME_OVERLAY, packageNames);
+ for (String key : properties.getKeyset()) {
+ final GamePackageConfiguration config =
+ GamePackageConfiguration.fromProperties(key, properties);
+ putConfig(config);
+ }
+ }
+ }
+
+ private void disableCompatScale(String packageName) {
+ final long uid = Binder.clearCallingIdentity();
+ try {
+ final HashSet<Long> disabledSet = new HashSet<>();
+ disabledSet.add(DOWNSCALED);
+ final CompatibilityChangeConfig changeConfig = new CompatibilityChangeConfig(
+ new Compatibility.ChangeConfig(new HashSet<>(), disabledSet));
+ // TODO: switch to new API provided by aosp/1599153 once merged
+ try {
+ mPlatformCompat.setOverridesForTest(changeConfig, packageName);
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Missing compat override permission", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to call IPlatformCompat#setOverridesForTest", e);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(uid);
+ }
+ }
+
+ private void enableCompatScale(String packageName, long scaleId) {
+ final long uid = Binder.clearCallingIdentity();
+ try {
+ final HashSet<Long> disabledSet = new HashSet<>();
+ final HashSet<Long> enabledSet = new HashSet<>();
+ disabledSet.add(DOWNSCALE_50);
+ disabledSet.add(DOWNSCALE_60);
+ disabledSet.add(DOWNSCALE_70);
+ disabledSet.add(DOWNSCALE_80);
+ disabledSet.add(DOWNSCALE_90);
+ disabledSet.remove(scaleId);
+ enabledSet.add(DOWNSCALED);
+ enabledSet.add(scaleId);
+ final CompatibilityChangeConfig changeConfig = new CompatibilityChangeConfig(
+ new Compatibility.ChangeConfig(enabledSet, disabledSet));
+ // TODO: switch to new API provided by aosp/1599153 once merged
+ try {
+ mPlatformCompat.setOverridesForTest(changeConfig, packageName);
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Missing compat override permission", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to call IPlatformCompat#setOverridesForTest", e);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(uid);
+ }
+ }
+
+ private void updateCompatModeDownscale(String packageName, @GameMode int gameMode) {
+ synchronized (mDeviceConfigLock) {
+ if (gameMode == GameManager.GAME_MODE_STANDARD
+ || gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
+ disableCompatScale(packageName);
+ Slog.v(TAG, "Disabling downscale");
+ return;
+ }
+ if (DEBUG) {
+ Slog.v(TAG, dumpDeviceConfigs());
+ }
+ final GamePackageConfiguration packageConfig = mConfigs.get(packageName);
+ if (packageConfig == null) {
+ Slog.w(TAG, "Package configuration not found for " + packageName);
+ return;
+ }
+ final GameModeConfiguration modeConfig = packageConfig.getGameModeConfiguration(
+ gameMode);
+ if (modeConfig == null) {
+ Slog.w(TAG, "Game mode " + gameMode + " not found for " + packageName);
+ return;
+ }
+ long scaleId = modeConfig.getCompatChangeId();
+ if (scaleId == 0) {
+ Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
+ + packageName);
+ return;
+ }
+ Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
+ enableCompatScale(packageName, scaleId);
+ }
+ }
+
+ private void putConfig(GamePackageConfiguration config) {
+ if (config.isValid()) {
+ if (DEBUG) {
+ Slog.i(TAG, "Adding config: " + config.toString());
+ }
+ mConfigs.put(config.getPackageName(), config);
+ } else {
+ Slog.w(TAG, "Invalid package config for "
+ + config.getPackageName() + ":" + config.toString());
+ }
+ }
+
+ private void registerPackageReceiver() {
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+ final BroadcastReceiver packageReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
+ final Uri data = intent.getData();
+ try {
+ final String packageName = data.getSchemeSpecificPart();
+ switch (intent.getAction()) {
+ case ACTION_PACKAGE_ADDED:
+ case ACTION_PACKAGE_CHANGED:
+ synchronized (mDeviceConfigLock) {
+ Properties properties = DeviceConfig.getProperties(
+ DeviceConfig.NAMESPACE_GAME_OVERLAY,
+ packageNameToKey(packageName));
+ for (String key : properties.getKeyset()) {
+ GamePackageConfiguration config =
+ GamePackageConfiguration.fromProperties(key,
+ properties);
+ putConfig(config);
+ }
+ }
+ break;
+ case ACTION_PACKAGE_REMOVED:
+ disableCompatScale(packageName);
+ mConfigs.remove(packageName);
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ } catch (NullPointerException e) {
+ Slog.e(TAG, "Failed to get package name for new package", e);
+ }
+ }
+ };
+ mContext.registerReceiver(packageReceiver, packageFilter);
+ }
+
+ private void registerDeviceConfigListener() {
+ mDeviceConfigListener = new DeviceConfigListener();
+ }
+
+ /**
+ * Valid package name characters are [a-zA-Z0-9_] with a '.' delimiter. Policy keys can only use
+ * [a-zA-Z0-9_] so we must handle periods. We do this by appending a '_' to any existing
+ * sequence of '_', then we replace all '.' chars with '_';
+ */
+ private static String packageNameToKey(String name) {
+ return name.replaceAll("(_+)", "_$1").replaceAll("\\.", "_");
+ }
+
+ /**
+ * Replace the last '_' in a sequence with '.' (this can be one or more chars), then replace the
+ * resulting special case '_.' with just '_' to get the original package name.
+ */
+ private static String keyToPackageName(String key) {
+ return key.replaceAll("(_)(?!\\1)", ".").replaceAll("_\\.", "_");
+ }
+
+ private String dumpDeviceConfigs() {
+ StringBuilder out = new StringBuilder();
+ for (String key : mConfigs.keySet()) {
+ out.append("[\nName: ").append(key)
+ .append("\nConfig: ").append(mConfigs.get(key).toString()).append("\n]");
+ }
+ return out.toString();
+ }
+
private static ServiceThread createServiceThread() {
ServiceThread handlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7bc7105..07ee5a2 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -94,6 +94,7 @@
import android.app.RuntimeAppOpAccessMessage;
import android.app.SyncNotedAppOp;
import android.app.admin.DevicePolicyManagerInternal;
+import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -131,6 +132,7 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.KeyValueListParser;
+import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pair;
import android.util.Pools;
@@ -159,6 +161,9 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.QuintFunction;
+import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
@@ -1999,8 +2004,10 @@
@Override
public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
- mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
+ final int callingUid = Binder.getCallingUid();
+ final boolean hasAllPackageAccess = mContext.checkPermission(
+ Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(),
+ Binder.getCallingUid(), null) == PackageManager.PERMISSION_GRANTED;
ArrayList<AppOpsManager.PackageOps> res = null;
synchronized (this) {
final int uidStateCount = mUidStates.size();
@@ -2016,11 +2023,14 @@
ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
if (resOps != null) {
if (res == null) {
- res = new ArrayList<AppOpsManager.PackageOps>();
+ res = new ArrayList<>();
}
AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
pkgOps.packageName, pkgOps.uidState.uid, resOps);
- res.add(resPackage);
+ // Caller can always see their packages and with a permission all.
+ if (hasAllPackageAccess || callingUid == pkgOps.uidState.uid) {
+ res.add(resPackage);
+ }
}
}
}
@@ -2031,8 +2041,7 @@
@Override
public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName,
int[] ops) {
- mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
+ enforceGetAppOpsStatsPermissionIfNeeded(uid,packageName);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return Collections.emptyList();
@@ -2054,6 +2063,22 @@
}
}
+ private void enforceGetAppOpsStatsPermissionIfNeeded(int uid, String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ // We get to access everything
+ if (callingUid == Process.myPid()) {
+ return;
+ }
+ // Apps can access their own data
+ if (uid == callingUid && packageName != null
+ && checkPackage(uid, packageName) == MODE_ALLOWED) {
+ return;
+ }
+ // Otherwise, you need a permission...
+ mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+ Binder.getCallingPid(), callingUid, null);
+ }
+
/**
* Verify that historical appop request arguments are valid.
*/
@@ -3078,15 +3103,52 @@
}
@Override
- public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
- String proxiedAttributionTag, int proxyUid, String proxyPackageName,
- String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage) {
- verifyIncomingUid(proxyUid);
+ public int noteProxyOperation(int code, AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation) {
+ final CheckOpsDelegate policy;
+ final CheckOpsDelegateDispatcher delegateDispatcher;
+ synchronized (AppOpsService.this) {
+ policy = mAppOpsPolicy;
+ delegateDispatcher = mCheckOpsDelegateDispatcher;
+ }
+ if (policy != null) {
+ if (delegateDispatcher != null) {
+ return policy.noteProxyOperation(code, attributionSource,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProxyOperation, delegateDispatcher::noteProxyOperationImpl);
+ } else {
+ return policy.noteProxyOperation(code, attributionSource,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProxyOperation, AppOpsService.this::noteProxyOperationImpl);
+ }
+ } else if (delegateDispatcher != null) {
+ delegateDispatcher.getCheckOpsDelegate().noteProxyOperation(code,
+ attributionSource, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation,
+ AppOpsService.this::noteProxyOperationImpl);
+ }
+ return noteProxyOperationImpl(code, attributionSource, shouldCollectAsyncNotedOp,
+ message, shouldCollectMessage,skipProxyOperation);
+ }
+
+ private int noteProxyOperationImpl(int code, AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation) {
+ final int proxyUid = attributionSource.getUid();
+ final String proxyPackageName = attributionSource.getPackageName();
+ final String proxyAttributionTag = attributionSource.getAttributionTag();
+ final int proxiedUid = attributionSource.getNextUid();
+ final String proxiedPackageName = attributionSource.getNextPackageName();
+ final String proxiedAttributionTag = attributionSource.getNextAttributionTag();
+
+ verifyIncomingProxyUid(attributionSource);
verifyIncomingOp(code);
verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
+ skipProxyOperation = resolveSkipProxyOperation(skipProxyOperation, attributionSource);
+
String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
if (resolveProxyPackageName == null) {
return AppOpsManager.MODE_IGNORED;
@@ -3097,19 +3159,23 @@
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
== PackageManager.PERMISSION_GRANTED || isSelfBlame;
- final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
- : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
- final int proxyMode = noteOperationUnchecked(code, proxyUid, resolveProxyPackageName,
- proxyAttributionTag, Process.INVALID_UID, null, null, proxyFlags,
- !isProxyTrusted, "proxy " + message, shouldCollectMessage);
- if (proxyMode != AppOpsManager.MODE_ALLOWED) {
- return proxyMode;
+ if (!skipProxyOperation) {
+ final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
+ : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
+
+ final int proxyMode = noteOperationUnchecked(code, proxyUid, resolveProxyPackageName,
+ proxyAttributionTag, Process.INVALID_UID, null, null, proxyFlags,
+ !isProxyTrusted, "proxy " + message, shouldCollectMessage);
+ if (proxyMode != AppOpsManager.MODE_ALLOWED) {
+ return proxyMode;
+ }
}
String resolveProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
if (resolveProxiedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
+
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
@@ -3558,16 +3624,56 @@
}
@Override
- public int startProxyOperation(IBinder clientId, int code, int proxiedUid,
- String proxiedPackageName, @Nullable String proxiedAttributionTag, int proxyUid,
- String proxyPackageName, @Nullable String proxyAttributionTag,
+ public int startProxyOperation(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation) {
+ final CheckOpsDelegate policy;
+ final CheckOpsDelegateDispatcher delegateDispatcher;
+ synchronized (AppOpsService.this) {
+ policy = mAppOpsPolicy;
+ delegateDispatcher = mCheckOpsDelegateDispatcher;
+ }
+ if (policy != null) {
+ if (delegateDispatcher != null) {
+ return policy.startProxyOperation(clientId, code, attributionSource,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation,
+ delegateDispatcher::startProxyOperationImpl);
+ } else {
+ return policy.startProxyOperation(clientId, code, attributionSource,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation,
+ AppOpsService.this::startProxyOperationImpl);
+ }
+ } else if (delegateDispatcher != null) {
+ delegateDispatcher.getCheckOpsDelegate().startProxyOperation(clientId, code,
+ attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation,
+ AppOpsService.this::startProxyOperationImpl);
+ }
+ return startProxyOperationImpl(clientId, code, attributionSource, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
+ }
+
+ private int startProxyOperationImpl(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage) {
- verifyIncomingUid(proxyUid);
+ boolean shouldCollectMessage, boolean skipProxyOperation) {
+ final int proxyUid = attributionSource.getUid();
+ final String proxyPackageName = attributionSource.getPackageName();
+ final String proxyAttributionTag = attributionSource.getAttributionTag();
+ final int proxiedUid = attributionSource.getNextUid();
+ final String proxiedPackageName = attributionSource.getNextPackageName();
+ final String proxiedAttributionTag = attributionSource.getNextAttributionTag();
+
+ verifyIncomingProxyUid(attributionSource);
verifyIncomingOp(code);
verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
+ skipProxyOperation = resolveSkipProxyOperation(skipProxyOperation, attributionSource);
+
String resolvedProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
if (resolvedProxyPackageName == null) {
return AppOpsManager.MODE_IGNORED;
@@ -3578,31 +3684,34 @@
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
== PackageManager.PERMISSION_GRANTED || isSelfBlame;
- final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
- : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
-
String resolvedProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
if (resolvedProxiedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
+
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
- // Test if the proxied operation will succeed before starting the proxy operation
- final int testProxiedMode = startOperationUnchecked(clientId, code, proxiedUid,
- resolvedProxiedPackageName, proxiedAttributionTag, proxyUid,
- resolvedProxyPackageName, proxyAttributionTag, proxiedFlags, startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, true);
- if (!shouldStartForMode(testProxiedMode, startIfModeDefault)) {
- return testProxiedMode;
- }
+ if (!skipProxyOperation) {
+ // Test if the proxied operation will succeed before starting the proxy operation
+ final int testProxiedMode = startOperationUnchecked(clientId, code, proxiedUid,
+ resolvedProxiedPackageName, proxiedAttributionTag, proxyUid,
+ resolvedProxyPackageName, proxyAttributionTag, proxiedFlags, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, true);
+ if (!shouldStartForMode(testProxiedMode, startIfModeDefault)) {
+ return testProxiedMode;
+ }
- final int proxyMode = startOperationUnchecked(clientId, code, proxyUid,
- resolvedProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null,
- proxyFlags, startIfModeDefault, !isProxyTrusted, "proxy " + message,
- shouldCollectMessage, false);
- if (!shouldStartForMode(proxyMode, startIfModeDefault)) {
- return proxyMode;
+ final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
+ : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
+
+ final int proxyMode = startOperationUnchecked(clientId, code, proxyUid,
+ resolvedProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null,
+ proxyFlags, startIfModeDefault, !isProxyTrusted, "proxy " + message,
+ shouldCollectMessage, false);
+ if (!shouldStartForMode(proxyMode, startIfModeDefault)) {
+ return proxyMode;
+ }
}
return startOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
@@ -3723,9 +3832,38 @@
}
@Override
- public void finishProxyOperation(IBinder clientId, int code, int proxiedUid,
- String proxiedPackageName, @Nullable String proxiedAttributionTag, int proxyUid,
- @Nullable String proxyPackageName, @Nullable String proxyAttributionTag) {
+ public void finishProxyOperation(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource) {
+ final CheckOpsDelegate policy;
+ final CheckOpsDelegateDispatcher delegateDispatcher;
+ synchronized (AppOpsService.this) {
+ policy = mAppOpsPolicy;
+ delegateDispatcher = mCheckOpsDelegateDispatcher;
+ }
+ if (policy != null) {
+ if (delegateDispatcher != null) {
+ policy.finishProxyOperation(clientId, code, attributionSource,
+ delegateDispatcher::finishProxyOperationImpl);
+ } else {
+ policy.finishProxyOperation(clientId, code, attributionSource,
+ AppOpsService.this::finishProxyOperationImpl);
+ }
+ } else if (delegateDispatcher != null) {
+ delegateDispatcher.getCheckOpsDelegate().finishProxyOperation(clientId, code,
+ attributionSource, AppOpsService.this::finishProxyOperationImpl);
+ }
+ finishProxyOperationImpl(clientId, code, attributionSource);
+ }
+
+ private Void finishProxyOperationImpl(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource) {
+ final int proxyUid = attributionSource.getUid();
+ final String proxyPackageName = attributionSource.getPackageName();
+ final String proxyAttributionTag = attributionSource.getAttributionTag();
+ final int proxiedUid = attributionSource.getNextUid();
+ final String proxiedPackageName = attributionSource.getNextPackageName();
+ final String proxiedAttributionTag = attributionSource.getNextAttributionTag();
+
verifyIncomingUid(proxyUid);
verifyIncomingOp(code);
verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
@@ -3733,7 +3871,7 @@
String resolvedProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
if (resolvedProxyPackageName == null) {
- return;
+ return null;
}
finishOperationUnchecked(clientId, code, proxyUid, resolvedProxyPackageName,
@@ -3741,11 +3879,13 @@
String resolvedProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
if (resolvedProxiedPackageName == null) {
- return;
+ return null;
}
finishOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
proxiedAttributionTag);
+
+ return null;
}
private void finishOperationUnchecked(IBinder clientId, int code, int uid, String packageName,
@@ -3953,6 +4093,20 @@
|| (permInfo.getProtectionFlags() & PROTECTION_FLAG_APPOP) != 0;
}
+ private void verifyIncomingProxyUid(@NonNull AttributionSource attributionSource) {
+ if (attributionSource.getUid() == Binder.getCallingUid()) {
+ return;
+ }
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return;
+ }
+ if (attributionSource.isTrusted(mContext)) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ }
+
private void verifyIncomingUid(int uid) {
if (uid == Binder.getCallingUid()) {
return;
@@ -3979,6 +4133,20 @@
}
}
+ private boolean resolveSkipProxyOperation(boolean requestsSkipProxyOperation,
+ @NonNull AttributionSource attributionSource) {
+ if (!requestsSkipProxyOperation) {
+ return false;
+ }
+ if (attributionSource.getUid() != Binder.getCallingUid()
+ && attributionSource.isTrusted(mContext)) {
+ return true;
+ }
+ return mContext.checkPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
private @Nullable UidState getUidStateLocked(int uid, boolean edit) {
UidState uidState = mUidStates.get(uid);
if (uidState == null) {
@@ -4098,7 +4266,6 @@
/**
* Create a restriction description matching the properties of the package.
*
- * @param context A context to use
* @param pkg The package to create the restriction description for
*
* @return The restriction matching the package
@@ -4141,15 +4308,25 @@
int callingUid = Binder.getCallingUid();
int userId = UserHandle.getUserId(uid);
-
RestrictionBypass bypass = null;
+
+ // Allow any attribution tag for resolvable uids
+ int pkgUid = resolveUid(packageName);
+ if (pkgUid != Process.INVALID_UID) {
+ // Special case for the shell which is a package but should be able
+ // to bypass app attribution tag restrictions.
+ if (pkgUid != UserHandle.getAppId(uid)) {
+ throw new SecurityException("Specified package " + packageName + " under uid "
+ + UserHandle.getAppId(uid) + " but it is really " + pkgUid);
+ }
+ return RestrictionBypass.UNRESTRICTED;
+ }
+
final long ident = Binder.clearCallingIdentity();
try {
- int pkgUid;
- AndroidPackage pkg = LocalServices.getService(PackageManagerInternal.class).getPackage(
- packageName);
boolean isAttributionTagValid = false;
-
+ AndroidPackage pkg = LocalServices.getService(PackageManagerInternal.class)
+ .getPackage(packageName);
if (pkg != null) {
if (attributionTag == null) {
isAttributionTagValid = true;
@@ -4166,20 +4343,7 @@
pkgUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
bypass = getBypassforPackage(pkg);
- } else {
- // Allow any attribution tag for resolvable uids
- isAttributionTagValid = true;
-
- pkgUid = resolveUid(packageName);
- if (pkgUid >= 0) {
- bypass = RestrictionBypass.UNRESTRICTED;
- }
}
- if (pkgUid != uid) {
- throw new SecurityException("Specified package " + packageName + " under uid " + uid
- + " but it is really " + pkgUid);
- }
-
if (!isAttributionTagValid) {
String msg = "attributionTag " + attributionTag + " not declared in"
+ " manifest of " + packageName;
@@ -4187,7 +4351,7 @@
if (mPlatformCompat.isChangeEnabledByPackageName(
SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName,
userId) && mPlatformCompat.isChangeEnabledByUid(
- SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, callingUid)) {
+ SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, callingUid)) {
throw new SecurityException(msg);
} else {
Slog.e(TAG, msg);
@@ -4199,6 +4363,11 @@
Binder.restoreCallingIdentity(ident);
}
+ if (pkgUid != uid) {
+ throw new SecurityException("Specified package " + packageName + " under uid " + uid
+ + " but it is really " + pkgUid);
+ }
+
return bypass;
}
@@ -6103,6 +6272,57 @@
}
@Override
+ public boolean isProxying(int op, @NonNull String proxyPackageName,
+ @NonNull String proxyAttributionTag, int proxiedUid,
+ @NonNull String proxiedPackageName) {
+ Objects.requireNonNull(proxyPackageName);
+ Objects.requireNonNull(proxiedPackageName);
+ Binder.withCleanCallingIdentity(() -> {
+ final List<AppOpsManager.PackageOps> packageOps = getOpsForPackage(proxiedUid,
+ proxiedPackageName, new int[] {op});
+ if (packageOps == null || packageOps.isEmpty()) {
+ return false;
+ }
+ final List<OpEntry> opEntries = packageOps.get(0).getOps();
+ if (opEntries.isEmpty()) {
+ return false;
+ }
+ final OpEntry opEntry = opEntries.get(0);
+ if (!opEntry.isRunning()) {
+ return false;
+ }
+ final OpEventProxyInfo proxyInfo = opEntry.getLastProxyInfo(
+ AppOpsManager.OP_FLAG_TRUSTED_PROXY
+ | AppOpsManager.OP_FLAG_UNTRUSTED_PROXY);
+ return proxyInfo != null && Binder.getCallingUid() == proxyInfo.getUid()
+ && proxyPackageName.equals(proxyInfo.getPackageName())
+ && Objects.equals(proxyAttributionTag, proxyInfo.getAttributionTag());
+ });
+ return false;
+ }
+
+ @Override
+ public void resetPackageOpsNoHistory(@NonNull String packageName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
+ "resetPackageOpsNoHistory");
+ synchronized (AppOpsService.this) {
+ final int uid = mPackageManagerInternal.getPackageUid(packageName, 0,
+ UserHandle.getCallingUserId());
+ if (uid == Process.INVALID_UID) {
+ return;
+ }
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null || uidState.pkgOps == null) {
+ return;
+ }
+ Ops removedOps = uidState.pkgOps.remove(packageName);
+ if (removedOps != null) {
+ scheduleFastWriteLocked();
+ }
+ }
+ }
+
+ @Override
public void setHistoryParameters(@AppOpsManager.HistoricalMode int mode,
long baseSnapshotInterval, int compressionStep) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
@@ -6455,6 +6675,7 @@
return Process.ROOT_UID;
case "shell":
case "dumpstate":
+ case "com.android.shell":
return Process.SHELL_UID;
case "media":
return Process.MEDIA_UID;
@@ -6831,5 +7052,29 @@
shouldCollectAsyncNotedOp, message, shouldCollectMessage,
AppOpsService.this::noteOperationImpl);
}
+
+ public int noteProxyOperationImpl(int code, @NonNull AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, boolean skipProxyOperation) {
+ return mCheckOpsDelegate.noteProxyOperation(code, attributionSource,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation,
+ AppOpsService.this::noteProxyOperationImpl);
+ }
+
+ public int startProxyOperationImpl(IBinder token, int code,
+ @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation) {
+ return mCheckOpsDelegate.startProxyOperation(token, code, attributionSource,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProxyOperation, AppOpsService.this::startProxyOperationImpl);
+ }
+
+ public Void finishProxyOperationImpl(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource) {
+ mCheckOpsDelegate.finishProxyOperation(clientId, code, attributionSource,
+ AppOpsService.this::finishProxyOperationImpl);
+ return null;
+ }
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 88dca0c..282a12d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -80,9 +80,12 @@
private final @NonNull AudioService mAudioService;
private final @NonNull Context mContext;
- /** Forced device usage for communications sent to AudioSystem */
- private AudioDeviceAttributes mPreferredCommunicationDevice;
+ /** ID for Communication strategy retrieved form audio policy manager */
private int mCommunicationStrategyId = -1;
+ /** Active communication device reported by audio policy manager */
+ private AudioDeviceInfo mActiveCommunicationDevice;
+ /** Last preferred device set for communication strategy */
+ private AudioDeviceAttributes mPreferredCommunicationDevice;
// Manages all connected devices, only ever accessed on the message loop
private final AudioDeviceInventory mDeviceInventory;
@@ -153,8 +156,9 @@
private void init() {
setupMessaging(mContext);
- mPreferredCommunicationDevice = null;
initCommunicationStrategyId();
+ mPreferredCommunicationDevice = null;
+ updateActiveCommunicationDevice();
mSystemServer.registerUserStartedReceiver(mContext);
}
@@ -300,7 +304,6 @@
+ " from API: " + eventSource)).printLog(TAG));
final boolean wasBtScoRequested = isBluetoothScoRequested();
- final boolean wasSpeakerphoneRequested = isSpeakerphoneRequested();
CommunicationRouteClient client;
@@ -341,16 +344,6 @@
mBtHelper.stopBluetoothSco(eventSource);
}
- if (wasSpeakerphoneRequested != isSpeakerphoneRequested()) {
- try {
- mContext.sendBroadcastAsUser(
- new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
- .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
- } catch (Exception e) {
- Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
- }
- }
-
sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
}
@@ -386,66 +379,119 @@
* Returns the device currently requested for communication use case.
* @return AudioDeviceInfo the requested device for communication.
*/
- AudioDeviceInfo getCommunicationDevice() {
+ /* package */ AudioDeviceInfo getCommunicationDevice() {
synchronized (mDeviceStateLock) {
- AudioDeviceAttributes device = requestedCommunicationDevice();
- if (device == null) {
- AudioAttributes attr =
- AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
- AudioSystem.STREAM_VOICE_CALL);
- List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr);
- if (devices.isEmpty()) {
- if (mAudioService.isPlatformVoice()) {
- Log.w(TAG, "getCommunicationDevice(): no device for phone strategy");
- }
- return null;
- }
- device = devices.get(0);
- }
- return AudioManager.getDeviceInfoFromTypeAndAddress(
- device.getType(), device.getAddress());
+ updateActiveCommunicationDevice();
+ return mActiveCommunicationDevice;
}
}
/**
- * Helper method on top of requestedCommunicationDevice() indicating if
+ * Updates currently active communication device (mActiveCommunicationDevice).
+ */
+ @GuardedBy("mDeviceStateLock")
+ void updateActiveCommunicationDevice() {
+ AudioDeviceAttributes device = preferredCommunicationDevice();
+ if (device == null) {
+ AudioAttributes attr =
+ AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
+ AudioSystem.STREAM_VOICE_CALL);
+ List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr);
+ if (devices.isEmpty()) {
+ if (mAudioService.isPlatformVoice()) {
+ Log.w(TAG,
+ "updateActiveCommunicationDevice(): no device for phone strategy");
+ }
+ mActiveCommunicationDevice = null;
+ return;
+ }
+ device = devices.get(0);
+ }
+ mActiveCommunicationDevice = AudioManager.getDeviceInfoFromTypeAndAddress(
+ device.getType(), device.getAddress());
+ }
+
+ /**
+ * Indicates if the device which type is passed as argument is currently resquested to be used
+ * for communication.
+ * @param deviceType the device type the query applies to.
+ * @return true if this device type is requested for communication.
+ */
+ private boolean isDeviceRequestedForCommunication(int deviceType) {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes device = requestedCommunicationDevice();
+ return device != null && device.getType() == deviceType;
+ }
+ }
+
+ /**
+ * Indicates if the device which type is passed as argument is currently either resquested
+ * to be used for communication or selected for an other reason (e.g bluetooth SCO audio
+ * is active for SCO device).
+ * @param deviceType the device type the query applies to.
+ * @return true if this device type is requested for communication.
+ */
+ private boolean isDeviceOnForCommunication(int deviceType) {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes device = preferredCommunicationDevice();
+ return device != null && device.getType() == deviceType;
+ }
+ }
+
+ /**
+ * Indicates if the device which type is passed as argument is active for communication.
+ * Active means not only currently used by audio policy manager for communication strategy
+ * but also explicitly requested for use by communication strategy.
+ * @param deviceType the device type the query applies to.
+ * @return true if this device type is requested for communication.
+ */
+ private boolean isDeviceActiveForCommunication(int deviceType) {
+ return mActiveCommunicationDevice != null
+ && mActiveCommunicationDevice.getType() == deviceType
+ && mPreferredCommunicationDevice != null
+ && mPreferredCommunicationDevice.getType() == deviceType;
+ }
+
+ /**
+ * Helper method on top of isDeviceRequestedForCommunication() indicating if
* speakerphone ON is currently requested or not.
* @return true if speakerphone ON requested, false otherwise.
*/
-
private boolean isSpeakerphoneRequested() {
- synchronized (mDeviceStateLock) {
- AudioDeviceAttributes device = requestedCommunicationDevice();
- return device != null
- && device.getType()
- == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
- }
+ return isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
}
/**
- * Indicates if active route selection for communication is speakerphone.
+ * Indicates if preferred route selection for communication is speakerphone.
* @return true if speakerphone is active, false otherwise.
*/
/*package*/ boolean isSpeakerphoneOn() {
- AudioDeviceAttributes device = getPreferredCommunicationDevice();
- if (device == null) {
- return false;
- }
- return device.getInternalType() == AudioSystem.DEVICE_OUT_SPEAKER;
+ return isDeviceOnForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
+ }
+
+ private boolean isSpeakerphoneActive() {
+ return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
}
/**
- * Helper method on top of requestedCommunicationDevice() indicating if
+ * Helper method on top of isDeviceRequestedForCommunication() indicating if
* Bluetooth SCO ON is currently requested or not.
* @return true if Bluetooth SCO ON is requested, false otherwise.
*/
/*package*/ boolean isBluetoothScoRequested() {
- synchronized (mDeviceStateLock) {
- AudioDeviceAttributes device = requestedCommunicationDevice();
- return device != null
- && device.getType()
- == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
- }
+ return isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
+ }
+
+ /**
+ * Indicates if preferred route selection for communication is Bluetooth SCO.
+ * @return true if Bluetooth SCO is preferred , false otherwise.
+ */
+ /*package*/ boolean isBluetoothScoOn() {
+ return isDeviceOnForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
+ }
+
+ /*package*/ boolean isBluetoothScoActive() {
+ return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
}
/*package*/ void setWiredDeviceConnectionState(int type,
@@ -593,18 +639,6 @@
}
}
- /**
- * Indicates if active route selection for communication is Bluetooth SCO.
- * @return true if Bluetooth SCO is active , false otherwise.
- */
- /*package*/ boolean isBluetoothScoOn() {
- AudioDeviceAttributes device = getPreferredCommunicationDevice();
- if (device == null) {
- return false;
- }
- return AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType());
- }
-
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.startWatchingRoutes(observer);
@@ -748,8 +782,8 @@
@GuardedBy("mDeviceStateLock")
private void dispatchCommunicationDevice() {
- AudioDeviceInfo device = getCommunicationDevice();
- int portId = (device == null) ? 0 : device.getId();
+ int portId = (mActiveCommunicationDevice == null) ? 0
+ : mActiveCommunicationDevice.getId();
if (portId == mCurCommunicationPortId) {
return;
}
@@ -1022,12 +1056,13 @@
pw.println(" " + prefix + "pid: " + cl.getPid() + " device: "
+ cl.getDevice() + " cb: " + cl.getBinder()); });
- pw.println("\n" + prefix + "mPreferredCommunicationDevice: "
+ pw.println("\n" + prefix + "Computed Preferred communication device: "
+ + preferredCommunicationDevice());
+ pw.println("\n" + prefix + "Applied Preferred communication device: "
+ mPreferredCommunicationDevice);
-
- AudioDeviceInfo device = getCommunicationDevice();
- pw.println(prefix + "Selected Communication Device: "
- + ((device == null) ? "None" : new AudioDeviceAttributes(device)));
+ pw.println(prefix + "Active communication device: "
+ + ((mActiveCommunicationDevice == null) ? "None"
+ : new AudioDeviceAttributes(mActiveCommunicationDevice)));
pw.println(prefix + "mCommunicationStrategyId: "
+ mCommunicationStrategyId);
@@ -1128,6 +1163,7 @@
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
initCommunicationStrategyId();
+ updateActiveCommunicationDevice();
mDeviceInventory.onRestoreDevices();
mBtHelper.onAudioServerDiedRestoreA2dp();
onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
@@ -1340,11 +1376,16 @@
final List<AudioDeviceAttributes> devices =
(List<AudioDeviceAttributes>) msg.obj;
setPreferredDevicesForStrategySync(strategy, devices);
-
+ if (strategy == mCommunicationStrategyId) {
+ onUpdatePhoneStrategyDevice(devices.isEmpty() ? null : devices.get(0));
+ }
} break;
case MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY: {
final int strategy = msg.arg1;
removePreferredDevicesForStrategySync(strategy);
+ if (strategy == mCommunicationStrategyId) {
+ onUpdatePhoneStrategyDevice(null);
+ }
} break;
case MSG_CHECK_MUTE_MUSIC:
checkMessagesMuteMusic(0);
@@ -1672,14 +1713,14 @@
}
/**
- * Determines which forced usage for communication should be sent to audio policy manager
+ * Determines which preferred device for phone strategy should be sent to audio policy manager
* as a function of current SCO audio activation state and active communication route requests.
* SCO audio state has the highest priority as it can result from external activation by
* telephony service.
* @return selected forced usage for communication.
*/
@GuardedBy("mDeviceStateLock")
- @Nullable private AudioDeviceAttributes getPreferredCommunicationDevice() {
+ @Nullable private AudioDeviceAttributes preferredCommunicationDevice() {
boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
if (btSCoOn) {
// Use the SCO device known to BtHelper so that it matches exactly
@@ -1692,8 +1733,7 @@
}
}
AudioDeviceAttributes device = requestedCommunicationDevice();
- if (device == null
- || AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType())) {
+ if (device == null || device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
// Do not indicate BT SCO selection if SCO is requested but SCO is not ON
return null;
}
@@ -1707,30 +1747,49 @@
// @GuardedBy("mSetModeLock")
@GuardedBy("mDeviceStateLock")
private void onUpdateCommunicationRoute(String eventSource) {
- mPreferredCommunicationDevice = getPreferredCommunicationDevice();
+ AudioDeviceAttributes preferredCommunicationDevice = preferredCommunicationDevice();
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "onUpdateCommunicationRoute, mPreferredCommunicationDevice: "
- + mPreferredCommunicationDevice + " eventSource: " + eventSource);
+ Log.v(TAG, "onUpdateCommunicationRoute, preferredCommunicationDevice: "
+ + preferredCommunicationDevice + " eventSource: " + eventSource);
}
AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
- "onUpdateCommunicationRoute, mPreferredCommunicationDevice: "
- + mPreferredCommunicationDevice + " eventSource: " + eventSource)));
+ "onUpdateCommunicationRoute, preferredCommunicationDevice: "
+ + preferredCommunicationDevice + " eventSource: " + eventSource)));
- if (mPreferredCommunicationDevice == null
- || !AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(
- mPreferredCommunicationDevice.getInternalType())) {
+ if (preferredCommunicationDevice == null
+ || preferredCommunicationDevice.getType() != AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
AudioSystem.setParameters("BT_SCO=off");
} else {
AudioSystem.setParameters("BT_SCO=on");
}
- if (mPreferredCommunicationDevice == null) {
+ if (preferredCommunicationDevice == null) {
postRemovePreferredDevicesForStrategy(mCommunicationStrategyId);
} else {
postSetPreferredDevicesForStrategy(
- mCommunicationStrategyId, Arrays.asList(mPreferredCommunicationDevice));
+ mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
}
- mAudioService.postUpdateRingerModeServiceInt();
- dispatchCommunicationDevice();
+ }
+
+ private void onUpdatePhoneStrategyDevice(AudioDeviceAttributes device) {
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ boolean wasSpeakerphoneActive = isSpeakerphoneActive();
+ mPreferredCommunicationDevice = device;
+ updateActiveCommunicationDevice();
+ if (wasSpeakerphoneActive != isSpeakerphoneActive()) {
+ try {
+ mContext.sendBroadcastAsUser(
+ new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+ UserHandle.ALL);
+ } catch (Exception e) {
+ Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
+ }
+ }
+ mAudioService.postUpdateRingerModeServiceInt();
+ dispatchCommunicationDevice();
+ }
+ }
}
private CommunicationRouteClient removeCommunicationRouteClient(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2ce60d0..16a9626 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4192,7 +4192,7 @@
final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
|| ringerMode == AudioManager.RINGER_MODE_SILENT;
final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
- && mDeviceBroker.isBluetoothScoOn();
+ && mDeviceBroker.isBluetoothScoActive();
// Ask audio policy engine to force use Bluetooth SCO channel if needed
final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
+ "/" + Binder.getCallingPid();
@@ -5600,7 +5600,7 @@
switch (mPlatformType) {
case AudioSystem.PLATFORM_VOICE:
if (isInCommunication()) {
- if (mDeviceBroker.isBluetoothScoOn()) {
+ if (mDeviceBroker.isBluetoothScoActive()) {
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
return AudioSystem.STREAM_BLUETOOTH_SCO;
} else {
@@ -5636,7 +5636,7 @@
}
default:
if (isInCommunication()) {
- if (mDeviceBroker.isBluetoothScoOn()) {
+ if (mDeviceBroker.isBluetoothScoActive()) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
return AudioSystem.STREAM_BLUETOOTH_SCO;
} else {
@@ -7907,7 +7907,24 @@
mmi.record();
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
clientId, callingPackageName, flags, sdk,
- forceFocusDuckingForAccessibility(aa, durationHint, uid));
+ forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/);
+ }
+
+ /** see {@link AudioManager#requestAudioFocusForTest(AudioFocusRequest, String, int, int)} */
+ public int requestAudioFocusForTest(AudioAttributes aa, int durationHint, IBinder cb,
+ IAudioFocusDispatcher fd, String clientId, String callingPackageName,
+ int fakeUid, int sdk) {
+ if (!enforceQueryAudioStateForTest("focus request")) {
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+ if (callingPackageName == null || clientId == null || aa == null) {
+ final String reason = "Invalid null parameter to request audio focus";
+ Log.e(TAG, reason);
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+ return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
+ clientId, callingPackageName, AudioManager.AUDIOFOCUS_FLAG_TEST,
+ sdk, false /*forceDuck*/, fakeUid);
}
public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa,
@@ -7927,6 +7944,15 @@
return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName);
}
+ /** see {@link AudioManager#abandonAudioFocusForTest(AudioFocusRequest, String)} */
+ public int abandonAudioFocusForTest(IAudioFocusDispatcher fd, String clientId,
+ AudioAttributes aa, String callingPackageName) {
+ if (!enforceQueryAudioStateForTest("focus abandon")) {
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+ return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName);
+ }
+
public void unregisterAudioFocusClient(String clientId) {
new MediaMetrics.Item(mMetricsId + "focus")
.set(MediaMetrics.Property.CLIENT_NAME, clientId)
@@ -7949,6 +7975,25 @@
return mMediaFocusControl.hasAudioFocusUsers();
}
+ /** see {@link AudioManager#getFadeOutDurationOnFocusLossMillis(AudioAttributes)} */
+ public long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) {
+ if (!enforceQueryAudioStateForTest("fade out duration")) {
+ return 0;
+ }
+ return mMediaFocusControl.getFadeOutDurationOnFocusLossMillis(aa);
+ }
+
+ private boolean enforceQueryAudioStateForTest(String mssg) {
+ if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
+ Manifest.permission.QUERY_AUDIO_STATE)) {
+ final String reason = "Doesn't have QUERY_AUDIO_STATE permission for "
+ + mssg + " test API";
+ Log.e(TAG, reason, new Exception());
+ return false;
+ }
+ return true;
+ }
+
//==========================================================================================
private boolean readCameraSoundForced() {
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index 9f0a2ba..e08bd67 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -97,6 +97,16 @@
return true;
}
+ static long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) {
+ if (ArrayUtils.contains(UNFADEABLE_CONTENT_TYPES, aa.getContentType())) {
+ return 0;
+ }
+ if (!ArrayUtils.contains(FADEABLE_USAGES, aa.getUsage())) {
+ return 0;
+ }
+ return FADE_OUT_DURATION_MS;
+ }
+
/**
* Map of uid (key) to faded out apps (value)
*/
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 1dcfdae..0310215 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -862,11 +862,13 @@
* @param forceDuck only true if
* {@link android.media.AudioFocusRequest.Builder#setFocusGain(int)} was set to true for
* accessibility.
+ * @param testUid ignored if flags is not AudioManager.AUDIOFOCUS_FLAG_TEST (strictly equals to)
+ * otherwise the UID being injected for testing
* @return
*/
protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
- int flags, int sdk, boolean forceDuck) {
+ int flags, int sdk, boolean forceDuck, int testUid) {
new MediaMetrics.Item(mMetricsId)
.setUid(Binder.getCallingUid())
.set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
@@ -878,8 +880,13 @@
//.set(MediaMetrics.Property.SDK, sdk)
.record();
+ // when using the test API, a fake UID can be injected (testUid is ignored otherwise)
+ // note that the test on flags is not a mask test on purpose, AUDIOFOCUS_FLAG_TEST is
+ // supposed to be alone in bitfield
+ final int uid = (flags == AudioManager.AUDIOFOCUS_FLAG_TEST)
+ ? testUid : Binder.getCallingUid();
mEventLogger.log((new AudioEventLogger.StringEvent(
- "requestAudioFocus() from uid/pid " + Binder.getCallingUid()
+ "requestAudioFocus() from uid/pid " + uid
+ "/" + Binder.getCallingPid()
+ " clientId=" + clientId + " callingPack=" + callingPackageName
+ " req=" + focusChangeHint
@@ -892,8 +899,10 @@
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
- if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
- callingPackageName) != AppOpsManager.MODE_ALLOWED) {
+ if ((flags != AudioManager.AUDIOFOCUS_FLAG_TEST)
+ // note we're using the real uid for appOp evaluation
+ && (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
+ callingPackageName) != AppOpsManager.MODE_ALLOWED)) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
@@ -910,7 +919,7 @@
final AudioFocusInfo afiForExtPolicy;
if (mFocusPolicy != null) {
// construct AudioFocusInfo as it will be communicated to audio focus policy
- afiForExtPolicy = new AudioFocusInfo(aa, Binder.getCallingUid(),
+ afiForExtPolicy = new AudioFocusInfo(aa, uid,
clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/,
flags, sdk);
} else {
@@ -980,7 +989,7 @@
removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
- clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk);
+ clientId, afdh, callingPackageName, uid, this, sdk);
if (mMultiAudioFocusEnabled
&& (focusChangeHint == AudioManager.AUDIOFOCUS_GAIN)) {
@@ -1143,6 +1152,13 @@
return mMultiAudioFocusEnabled;
}
+ /*package*/ long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) {
+ if (!ENFORCE_FADEOUT_FOR_FOCUS_LOSS) {
+ return 0;
+ }
+ return FadeOutManager.getFadeOutDurationOnFocusLossMillis(aa);
+ }
+
private void dumpMultiAudioFocus(PrintWriter pw) {
pw.println("Multi Audio Focus enabled :" + mMultiAudioFocusEnabled);
if (!mMultiAudioFocusList.isEmpty()) {
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 47c91e6..e71219f 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -704,6 +704,7 @@
// find which players to fade out
synchronized (mPlayerLock) {
if (mPlayers.isEmpty()) {
+ if (DEBUG) { Log.v(TAG, "no players to fade out"); }
return false;
}
// check if this UID needs to be faded out (return false if not), and gather list of
@@ -731,8 +732,6 @@
apcsToFadeOut.add(apc);
}
}
- //###
- //mDuckingManager.duckUid(loser.getClientUid(), apcsToFadeOut);
if (loserHasActivePlayers) {
mFadingManager.fadeOutUid(loser.getClientUid(), apcsToFadeOut);
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 63e7b4b..70f26ac 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -766,15 +766,18 @@
public long[] getAuthenticatorIds(int callingUserId) {
checkInternalPermission();
- final List<Long> ids = new ArrayList<>();
+ final List<Long> authenticatorIds = new ArrayList<>();
for (BiometricSensor sensor : mSensors) {
try {
- final long id = sensor.impl.getAuthenticatorId(callingUserId);
- if (Utils.isAtLeastStrength(sensor.getCurrentStrength(),
- Authenticators.BIOMETRIC_STRONG) && id != 0) {
- ids.add(id);
+ final boolean hasEnrollments = sensor.impl.hasEnrolledTemplates(callingUserId,
+ getContext().getOpPackageName());
+ final long authenticatorId = sensor.impl.getAuthenticatorId(callingUserId);
+ if (hasEnrollments && Utils.isAtLeastStrength(sensor.getCurrentStrength(),
+ Authenticators.BIOMETRIC_STRONG)) {
+ authenticatorIds.add(authenticatorId);
} else {
- Slog.d(TAG, "Sensor " + sensor + ", sensorId " + id
+ Slog.d(TAG, "Sensor " + sensor + ", sensorId " + sensor.id
+ + ", hasEnrollments: " + hasEnrollments
+ " cannot participate in Keystore operations");
}
} catch (RemoteException e) {
@@ -782,9 +785,9 @@
}
}
- long[] result = new long[ids.size()];
- for (int i = 0; i < ids.size(); i++) {
- result[i] = ids.get(i);
+ long[] result = new long[authenticatorIds.size()];
+ for (int i = 0; i < authenticatorIds.size(); i++) {
+ result[i] = authenticatorIds.get(i);
}
return result;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 3cfaaf7..677ea5d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -52,7 +52,8 @@
*
* @param clientMonitor Reference of the ClientMonitor that is starting.
*/
- default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {}
+ default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ }
/**
* Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
@@ -63,10 +64,11 @@
* @param clientMonitor Reference of the ClientMonitor that finished.
* @param success True if the operation completed successfully.
*/
- default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {}
+ default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
+ }
}
- protected final int mSequentialId;
+ private final int mSequentialId;
@NonNull private final Context mContext;
private final int mTargetUserId;
@NonNull private final String mOwner;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 07d173c..0c883b0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -92,7 +92,7 @@
@Override
protected void startHalOperation() {
try {
- mCancellationSignal = getFreshDaemon().authenticate(mSequentialId, mOperationId);
+ mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 0eb51fd..58eb3ba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -160,7 +160,7 @@
features = new byte[0];
}
- mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
+ mCancellationSignal = getFreshDaemon().enroll(
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken),
EnrollmentType.DEFAULT, features, mPreviewSurface);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index 7a846f5..8cbb896 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -42,7 +42,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().generateChallenge(mSequentialId);
+ getFreshDaemon().generateChallenge();
} catch (RemoteException e) {
Slog.e(TAG, "Unable to generateChallenge", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index 773647b..af826c2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -56,7 +56,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().getAuthenticatorId(mSequentialId);
+ getFreshDaemon().getAuthenticatorId();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
index 75888a5..0ece884 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java
@@ -48,7 +48,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().enumerateEnrollments(mSequentialId);
+ getFreshDaemon().enumerateEnrollments();
} 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/face/aidl/FaceInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
index 855ee1d..405e2b2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java
@@ -40,7 +40,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().invalidateAuthenticatorId(mSequentialId);
+ getFreshDaemon().invalidateAuthenticatorId();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
index 48796c1..ba678f3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
@@ -53,7 +53,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().removeEnrollments(mSequentialId, mBiometricIds);
+ getFreshDaemon().removeEnrollments(mBiometricIds);
} 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/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index ce728bc..5e57950 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -71,7 +71,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
+ getFreshDaemon().resetLockout(mHardwareAuthToken);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to reset lockout", e);
mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
index 2863f9f..2294173 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
@@ -45,7 +45,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().revokeChallenge(mSequentialId, mChallenge);
+ getFreshDaemon().revokeChallenge(mChallenge);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to revokeChallenge", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
index 8d3853b..06328e3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
@@ -44,7 +44,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().close(mSequentialId);
+ getFreshDaemon().close();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
getCallback().onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 768f464..ee36775 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -138,11 +138,6 @@
}
@Override
- public void onStateChanged(int cookie, byte state) {
- // TODO(b/162973174)
- }
-
- @Override
public void onChallengeGenerated(long challenge) {
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
@@ -535,7 +530,7 @@
if (mCurrentSession != null && mCurrentSession.mSession != null) {
// TODO(181984005): This should be scheduled instead of directly invoked
Slog.d(mTag, "Closing old session");
- mCurrentSession.mSession.close(888 /* cookie */);
+ mCurrentSession.mSession.close();
}
} catch (RemoteException e) {
Slog.e(mTag, "RemoteException", e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index 36327bb..c63af7e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -22,7 +22,6 @@
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.ISessionCallback;
import android.hardware.biometrics.face.SensorProps;
-import android.hardware.biometrics.face.SessionState;
import android.hardware.common.NativeHandle;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
@@ -45,21 +44,21 @@
return new ISession.Stub() {
@Override
- public void generateChallenge(int cookie) throws RemoteException {
- Slog.w(TAG, "generateChallenge, cookie: " + cookie);
+ public void generateChallenge() throws RemoteException {
+ Slog.w(TAG, "generateChallenge");
cb.onChallengeGenerated(0L);
}
@Override
- public void revokeChallenge(int cookie, long challenge) throws RemoteException {
- Slog.w(TAG, "revokeChallenge: " + challenge + ", cookie: " + cookie);
+ public void revokeChallenge(long challenge) throws RemoteException {
+ Slog.w(TAG, "revokeChallenge: " + challenge);
cb.onChallengeRevoked(challenge);
}
@Override
- public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
+ public ICancellationSignal enroll(HardwareAuthToken hat,
byte enrollmentType, byte[] features, NativeHandle previewSurface) {
- Slog.w(TAG, "enroll, cookie: " + cookie);
+ Slog.w(TAG, "enroll");
return new ICancellationSignal.Stub() {
@Override
public void cancel() throws RemoteException {
@@ -69,8 +68,8 @@
}
@Override
- public ICancellationSignal authenticate(int cookie, long operationId) {
- Slog.w(TAG, "authenticate, cookie: " + cookie);
+ public ICancellationSignal authenticate(long operationId) {
+ Slog.w(TAG, "authenticate");
return new ICancellationSignal.Stub() {
@Override
public void cancel() throws RemoteException {
@@ -80,8 +79,8 @@
}
@Override
- public ICancellationSignal detectInteraction(int cookie) {
- Slog.w(TAG, "detectInteraction, cookie: " + cookie);
+ public ICancellationSignal detectInteraction() {
+ Slog.w(TAG, "detectInteraction");
return new ICancellationSignal.Stub() {
@Override
public void cancel() throws RemoteException {
@@ -91,51 +90,51 @@
}
@Override
- public void enumerateEnrollments(int cookie) throws RemoteException {
- Slog.w(TAG, "enumerateEnrollments, cookie: " + cookie);
+ public void enumerateEnrollments() throws RemoteException {
+ Slog.w(TAG, "enumerateEnrollments");
cb.onEnrollmentsEnumerated(new int[0]);
}
@Override
- public void removeEnrollments(int cookie, int[] enrollmentIds) throws RemoteException {
- Slog.w(TAG, "removeEnrollments, cookie: " + cookie);
+ public void removeEnrollments(int[] enrollmentIds) throws RemoteException {
+ Slog.w(TAG, "removeEnrollments");
cb.onEnrollmentsRemoved(enrollmentIds);
}
@Override
- public void getFeatures(int cookie, int enrollmentId) throws RemoteException {
- Slog.w(TAG, "getFeatures, cookie: " + cookie);
+ public void getFeatures(int enrollmentId) throws RemoteException {
+ Slog.w(TAG, "getFeatures");
cb.onFeaturesRetrieved(new byte[0], enrollmentId);
}
@Override
- public void setFeature(int cookie, HardwareAuthToken hat, int enrollmentId,
+ public void setFeature(HardwareAuthToken hat, int enrollmentId,
byte feature, boolean enabled) throws RemoteException {
- Slog.w(TAG, "setFeature, cookie: " + cookie);
+ Slog.w(TAG, "setFeature");
cb.onFeatureSet(enrollmentId, feature);
}
@Override
- public void getAuthenticatorId(int cookie) throws RemoteException {
- Slog.w(TAG, "getAuthenticatorId, cookie: " + cookie);
+ public void getAuthenticatorId() throws RemoteException {
+ Slog.w(TAG, "getAuthenticatorId");
cb.onAuthenticatorIdRetrieved(0L);
}
@Override
- public void invalidateAuthenticatorId(int cookie) throws RemoteException {
- Slog.w(TAG, "invalidateAuthenticatorId, cookie: " + cookie);
+ public void invalidateAuthenticatorId() throws RemoteException {
+ Slog.w(TAG, "invalidateAuthenticatorId");
cb.onAuthenticatorIdInvalidated(0L);
}
@Override
- public void resetLockout(int cookie, HardwareAuthToken hat) throws RemoteException {
- Slog.w(TAG, "resetLockout, cookie: " + cookie);
+ public void resetLockout(HardwareAuthToken hat) throws RemoteException {
+ Slog.w(TAG, "resetLockout");
cb.onLockoutCleared();
}
@Override
- public void close(int cookie) throws RemoteException {
- Slog.w(TAG, "close, cookie: " + cookie);
+ public void close() throws RemoteException {
+ Slog.w(TAG, "close");
cb.onSessionClosed();
}
};
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 50756c8..79e361f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -528,6 +528,8 @@
}
}
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
mSensorId, mCurrentChallengeOwner);
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
index 76a47d3..4e5d12d 100644
--- 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
@@ -92,7 +92,7 @@
UdfpsHelper.showUdfpsOverlay(getSensorId(), Utils.getUdfpsAuthReason(this),
mUdfpsOverlayController, this);
try {
- mCancellationSignal = getFreshDaemon().authenticate(mSequentialId, mOperationId);
+ mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
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
index 620a9cf..9e9d0ee 100644
--- 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
@@ -74,7 +74,7 @@
IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD,
mUdfpsOverlayController, this);
try {
- mCancellationSignal = getFreshDaemon().detectInteraction(mSequentialId);
+ mCancellationSignal = getFreshDaemon().detectInteraction();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting finger detect", e);
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
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 63fa66c..fd4aece 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
@@ -123,7 +123,7 @@
UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
mUdfpsOverlayController, this);
try {
- mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
+ mCancellationSignal = getFreshDaemon().enroll(
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 3c9cced..83c6421 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
@@ -44,7 +44,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().generateChallenge(mSequentialId);
+ getFreshDaemon().generateChallenge();
} catch (RemoteException e) {
Slog.e(TAG, "Unable to generateChallenge", e);
}
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
index ce1a318..ed2345e 100644
--- 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
@@ -56,7 +56,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().getAuthenticatorId(mSequentialId);
+ getFreshDaemon().getAuthenticatorId();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
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
index c930360..e20544a 100644
--- 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
@@ -48,7 +48,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().enumerateEnrollments(mSequentialId);
+ getFreshDaemon().enumerateEnrollments();
} 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/FingerprintInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
index 80d1a0f..6cd2ef1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java
@@ -40,7 +40,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().invalidateAuthenticatorId(mSequentialId);
+ getFreshDaemon().invalidateAuthenticatorId();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
mCallback.onClientFinished(this, false /* success */);
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
index c622208..9a9d6ab 100644
--- 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
@@ -54,7 +54,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().removeEnrollments(mSequentialId, mBiometricIds);
+ getFreshDaemon().removeEnrollments(mBiometricIds);
} 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 adffba2..b00c592 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
@@ -71,7 +71,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
+ getFreshDaemon().resetLockout(mHardwareAuthToken);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to reset lockout", e);
mCallback.onClientFinished(this, false /* success */);
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 ebb4fe6..d9bf1c3 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
@@ -45,7 +45,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().revokeChallenge(mSequentialId, mChallenge);
+ getFreshDaemon().revokeChallenge(mChallenge);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to revokeChallenge", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
index ba81357..7055d65 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
@@ -44,7 +44,7 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().close(mSequentialId);
+ getFreshDaemon().close();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
getCallback().onClientFinished(this, false /* success */);
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 cd12d02..4862d849 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
@@ -136,11 +136,6 @@
}
@Override
- public void onStateChanged(int cookie, byte state) {
- // TODO(b/162973174)
- }
-
- @Override
public void onChallengeGenerated(long challenge) {
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
@@ -515,7 +510,7 @@
if (mCurrentSession != null && mCurrentSession.mSession != null) {
// TODO(181984005): This should be scheduled instead of directly invoked
Slog.d(mTag, "Closing old session");
- mCurrentSession.mSession.close(999 /* cookie */);
+ mCurrentSession.mSession.close();
}
} catch (RemoteException e) {
Slog.e(mTag, "RemoteException", e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
index 31fc068..abc3597 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
@@ -22,7 +22,6 @@
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.ISessionCallback;
import android.hardware.biometrics.fingerprint.SensorProps;
-import android.hardware.biometrics.fingerprint.SessionState;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
import android.util.Slog;
@@ -45,20 +44,20 @@
return new ISession.Stub() {
@Override
- public void generateChallenge(int cookie) throws RemoteException {
- Slog.w(TAG, "generateChallenge, cookie: " + cookie);
+ public void generateChallenge() throws RemoteException {
+ Slog.w(TAG, "generateChallenge");
cb.onChallengeGenerated(0L);
}
@Override
- public void revokeChallenge(int cookie, long challenge) throws RemoteException {
- Slog.w(TAG, "revokeChallenge: " + challenge + ", cookie: " + cookie);
+ public void revokeChallenge(long challenge) throws RemoteException {
+ Slog.w(TAG, "revokeChallenge: " + challenge);
cb.onChallengeRevoked(challenge);
}
@Override
- public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) {
- Slog.w(TAG, "enroll, cookie: " + cookie);
+ public ICancellationSignal enroll(HardwareAuthToken hat) {
+ Slog.w(TAG, "enroll");
return new ICancellationSignal.Stub() {
@Override
public void cancel() throws RemoteException {
@@ -68,8 +67,8 @@
}
@Override
- public ICancellationSignal authenticate(int cookie, long operationId) {
- Slog.w(TAG, "authenticate, cookie: " + cookie);
+ public ICancellationSignal authenticate(long operationId) {
+ Slog.w(TAG, "authenticate");
return new ICancellationSignal.Stub() {
@Override
public void cancel() throws RemoteException {
@@ -79,8 +78,8 @@
}
@Override
- public ICancellationSignal detectInteraction(int cookie) {
- Slog.w(TAG, "detectInteraction, cookie: " + cookie);
+ public ICancellationSignal detectInteraction() {
+ Slog.w(TAG, "detectInteraction");
return new ICancellationSignal.Stub() {
@Override
public void cancel() throws RemoteException {
@@ -90,38 +89,38 @@
}
@Override
- public void enumerateEnrollments(int cookie) throws RemoteException {
- Slog.w(TAG, "enumerateEnrollments, cookie: " + cookie);
+ public void enumerateEnrollments() throws RemoteException {
+ Slog.w(TAG, "enumerateEnrollments");
cb.onEnrollmentsEnumerated(new int[0]);
}
@Override
- public void removeEnrollments(int cookie, int[] enrollmentIds) throws RemoteException {
- Slog.w(TAG, "removeEnrollments, cookie: " + cookie);
+ public void removeEnrollments(int[] enrollmentIds) throws RemoteException {
+ Slog.w(TAG, "removeEnrollments");
cb.onEnrollmentsRemoved(enrollmentIds);
}
@Override
- public void getAuthenticatorId(int cookie) throws RemoteException {
- Slog.w(TAG, "getAuthenticatorId, cookie: " + cookie);
+ public void getAuthenticatorId() throws RemoteException {
+ Slog.w(TAG, "getAuthenticatorId");
cb.onAuthenticatorIdRetrieved(0L);
}
@Override
- public void invalidateAuthenticatorId(int cookie) throws RemoteException {
- Slog.w(TAG, "invalidateAuthenticatorId, cookie: " + cookie);
+ public void invalidateAuthenticatorId() throws RemoteException {
+ Slog.w(TAG, "invalidateAuthenticatorId");
cb.onAuthenticatorIdInvalidated(0L);
}
@Override
- public void resetLockout(int cookie, HardwareAuthToken hat) throws RemoteException {
- Slog.w(TAG, "resetLockout, cookie: " + cookie);
+ public void resetLockout(HardwareAuthToken hat) throws RemoteException {
+ Slog.w(TAG, "resetLockout");
cb.onLockoutCleared();
}
@Override
- public void close(int cookie) throws RemoteException {
- Slog.w(TAG, "close, cookie: " + cookie);
+ public void close() throws RemoteException {
+ Slog.w(TAG, "close");
cb.onSessionClosed();
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 00b39f1..cd24576 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -15,17 +15,33 @@
*/
package com.android.server.camera;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.os.Build.VERSION_CODES.M;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.TaskStackListener;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
import android.hardware.CameraSessionStats;
import android.hardware.CameraStreamStats;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceProxy;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.display.DisplayManager;
import android.media.AudioManager;
import android.metrics.LogMaker;
import android.nfc.INfcAdapter;
@@ -41,7 +57,10 @@
import android.stats.camera.nano.CameraProtos.CameraStreamProto;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
+import android.view.Display;
+import android.view.Surface;
import com.android.internal.annotations.GuardedBy;
import com.android.framework.protobuf.nano.MessageNano;
@@ -61,6 +80,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -203,6 +223,63 @@
}
}
+ private final TaskStateHandler mTaskStackListener = new TaskStateHandler();
+
+ private final class TaskInfo {
+ private int frontTaskId;
+ private boolean isResizeable;
+ private boolean isFixedOrientationLandscape;
+ private boolean isFixedOrientationPortrait;
+ private int displayId;
+ }
+
+ private final class TaskStateHandler extends TaskStackListener {
+ private final Object mMapLock = new Object();
+
+ // maps the current top level task id to its corresponding package name
+ @GuardedBy("mMapLock")
+ private final ArrayMap<String, TaskInfo> mTaskInfoMap = new ArrayMap<>();
+
+ @Override
+ public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
+ throws RemoteException {
+ synchronized (mMapLock) {
+ TaskInfo info = new TaskInfo();
+ info.frontTaskId = taskInfo.taskId;
+ info.isResizeable = taskInfo.isResizeable;
+ info.displayId = taskInfo.displayId;
+ info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
+ taskInfo.topActivityInfo.screenOrientation);
+ info.isFixedOrientationPortrait = ActivityInfo.isFixedOrientationPortrait(
+ taskInfo.topActivityInfo.screenOrientation);
+ mTaskInfoMap.put(taskInfo.topActivityInfo.packageName, info);
+ }
+ }
+
+ @Override
+ public void onTaskRemoved(int taskId) throws RemoteException {
+ synchronized (mMapLock) {
+ for (Map.Entry<String, TaskInfo> entry : mTaskInfoMap.entrySet()){
+ if (entry.getValue().frontTaskId == taskId) {
+ mTaskInfoMap.remove(entry.getKey());
+ break;
+ }
+ }
+ }
+ }
+
+ public @Nullable TaskInfo getFrontTaskInfo(String packageName) {
+ synchronized (mMapLock) {
+ if (mTaskInfoMap.containsKey(packageName)) {
+ return mTaskInfoMap.get(packageName);
+ }
+ }
+
+ Log.e(TAG, "Top task with package name: " + packageName + " not found!");
+ return null;
+ }
+ };
+
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -229,6 +306,102 @@
};
private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
+ private boolean isMOrBelow(Context ctx, String packageName) {
+ try {
+ return ctx.getPackageManager().getPackageInfo(
+ packageName, 0).applicationInfo.targetSdkVersion <= M;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG,"Package name not found!");
+ }
+ return false;
+ }
+
+ /**
+ * Gets whether crop-rotate-scale is needed.
+ */
+ private boolean getNeedCropRotateScale(Context ctx, String packageName,
+ @Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing) {
+ if (taskInfo == null) {
+ return false;
+ }
+
+ // External cameras do not need crop-rotate-scale.
+ if (lensFacing != CameraMetadata.LENS_FACING_FRONT
+ && lensFacing != CameraMetadata.LENS_FACING_BACK) {
+ Log.v(TAG, "lensFacing=" + lensFacing + ". Crop-rotate-scale is disabled.");
+ return false;
+ }
+
+ // Only enable the crop-rotate-scale workaround if the app targets M or below and is not
+ // resizeable.
+ if ((ctx != null) && !isMOrBelow(ctx, packageName) && taskInfo.isResizeable) {
+ Slog.v(TAG,
+ "The activity is N or above and claims to support resizeable-activity. "
+ + "Crop-rotate-scale is disabled.");
+ return false;
+ }
+
+ DisplayManager displayManager = ctx.getSystemService(DisplayManager.class);
+ Display display = displayManager.getDisplay(taskInfo.displayId);
+ int rotation = display.getRotation();
+ int rotationDegree = 0;
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ rotationDegree = 0;
+ break;
+ case Surface.ROTATION_90:
+ rotationDegree = 90;
+ break;
+ case Surface.ROTATION_180:
+ rotationDegree = 180;
+ break;
+ case Surface.ROTATION_270:
+ rotationDegree = 270;
+ break;
+ }
+
+ // Here we only need to know whether the camera is landscape or portrait. Therefore we
+ // don't need to consider whether it is a front or back camera. The formula works for
+ // both.
+ boolean landscapeCamera = ((rotationDegree + sensorOrientation) % 180 == 0);
+ Slog.v(TAG,
+ "Display.getRotation()=" + rotationDegree
+ + " CameraCharacteristics.SENSOR_ORIENTATION=" + sensorOrientation
+ + " isFixedOrientationPortrait=" + taskInfo.isFixedOrientationPortrait
+ + " isFixedOrientationLandscape=" +
+ taskInfo.isFixedOrientationLandscape);
+ // We need to do crop-rotate-scale when camera is landscape and activity is portrait or
+ // vice versa.
+ if ((taskInfo.isFixedOrientationPortrait && landscapeCamera)
+ || (taskInfo.isFixedOrientationLandscape && !landscapeCamera)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isRotateAndCropOverrideNeeded(String packageName, int sensorOrientation,
+ int lensFacing) {
+ if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
+ Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
+ " camera service UID!");
+ return false;
+ }
+
+ // A few remaining todos:
+ // 1) Do the same check when working in WM compatible mode. The sequence needs
+ // to be adjusted and use orientation events as triggers for all active camera
+ // clients.
+ // 2) Modify the sensor orientation in camera characteristics along with any 3A regions
+ // in capture requests/results to account for thea physical rotation. The former
+ // is somewhat tricky as it assumes that camera clients always check for the current
+ // value by retrieving the camera characteristics from the camera device.
+ return getNeedCropRotateScale(mContext, packageName,
+ mTaskStackListener.getFrontTaskInfo(packageName), sensorOrientation,
+ lensFacing);
+ }
+
@Override
public void pingForUserUpdate() {
if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
@@ -350,6 +523,12 @@
publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
publishLocalService(CameraServiceProxy.class, this);
+ try {
+ ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register task stack listener!");
+ }
+
CameraStatsJobService.schedule(mContext);
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 874e9a6..e148775 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -132,6 +132,18 @@
}
}
+ private byte[] receiveMessage() throws IOException {
+ final int size = Integer.reverseBytes(mPipe.readInt());
+ final byte[] receivedData = new byte[size];
+ mPipe.readFully(receivedData);
+ return receivedData;
+ }
+
+ private void sendMessage(byte[] message) throws IOException {
+ mPipe.writeInt(Integer.reverseBytes(message.length));
+ mPipe.write(message);
+ }
+
public HostClipboardMonitor(HostClipboardCallback cb) {
mHostClipboardCallback = cb;
}
@@ -146,10 +158,8 @@
while ((mPipe == null) && !openPipe()) {
Thread.sleep(100);
}
- int size = mPipe.readInt();
- size = Integer.reverseBytes(size);
- byte[] receivedData = new byte[size];
- mPipe.readFully(receivedData);
+
+ final byte[] receivedData = receiveMessage();
mHostClipboardCallback.onHostClipboardUpdated(
new String(receivedData));
} catch (IOException e) {
@@ -161,8 +171,7 @@
public void setHostClipboard(String content) {
try {
if (mPipe != null) {
- mPipe.writeInt(Integer.reverseBytes(content.getBytes().length));
- mPipe.write(content.getBytes());
+ sendMessage(content.getBytes());
}
} catch(IOException e) {
Slog.e("HostClipboardMonitor",
@@ -470,7 +479,7 @@
synchronized (mLock) {
addActiveOwnerLocked(intendingUid, pkg);
PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
- maybeNotifyLocked(pkg, intendingUid, intendingUserId, clipboard);
+ showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
return clipboard.primaryClip;
}
}
@@ -934,7 +943,7 @@
private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,
@UserIdInt int userId, boolean shouldNoteOp) {
- boolean allowed = false;
+ boolean allowed;
// First, verify package ownership to ensure use below is safe.
mAppOps.checkPackage(uid, callingPackage);
@@ -943,15 +952,9 @@
if (mPm.checkPermission(android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND,
callingPackage) == PackageManager.PERMISSION_GRANTED) {
allowed = true;
- }
- // The default IME is always allowed to access the clipboard.
- String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD, userId);
- if (!TextUtils.isEmpty(defaultIme)) {
- final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();
- if (imePkg.equals(callingPackage)) {
- allowed = true;
- }
+ } else {
+ // The default IME is always allowed to access the clipboard.
+ allowed = isDefaultIme(userId, callingPackage);
}
switch (op) {
@@ -1008,12 +1011,24 @@
return appOpsResult == AppOpsManager.MODE_ALLOWED;
}
+ private boolean isDefaultIme(int userId, String packageName) {
+ String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD, userId);
+ if (!TextUtils.isEmpty(defaultIme)) {
+ final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();
+ return imePkg.equals(packageName);
+ }
+ return false;
+ }
+
/**
- * Potentially notifies the user (via a toast) about an app accessing the clipboard.
- * TODO(b/167676460): STOPSHIP as we don't want this code as-is to launch. Just an experiment.
+ * Shows a toast to inform the user that an app has accessed the clipboard. This is only done if
+ * the setting is enabled, and if the accessing app is not the source of the data and is not the
+ * IME, the content capture service, or the autofill service. The notification is also only
+ * shown once per clip for each app.
*/
@GuardedBy("mLock")
- private void maybeNotifyLocked(String callingPackage, int uid, @UserIdInt int userId,
+ private void showAccessNotificationLocked(String callingPackage, int uid, @UserIdInt int userId,
PerUserClipboard clipboard) {
if (clipboard.primaryClip == null) {
return;
@@ -1029,15 +1044,9 @@
if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {
return;
}
- // Exclude some special cases. It's a bit wasteful to check these again here, but for now
- // beneficial to have all the logic contained in this single (probably temporary) method.
- String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD, userId);
- if (!TextUtils.isEmpty(defaultIme)) {
- final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();
- if (imePkg.equals(callingPackage)) {
- return;
- }
+ // Exclude special cases: IME, ContentCapture, Autofill.
+ if (isDefaultIme(userId, callingPackage)) {
+ return;
}
if (mContentCaptureInternal != null
&& mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId)) {
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index ae9b001..d29a0c7 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -23,7 +23,9 @@
import android.annotation.Nullable;
import android.app.compat.PackageOverride;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -60,6 +62,15 @@
static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id.
/**
+ * An overridable change ID to be used only in the CTS test for this SystemApi
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ static final long CTS_SYSTEM_API_OVERRIDABLE_CHANGEID = 174043039; // This is a bug id.
+
+
+ /**
* Callback listener for when compat changes are updated for a package.
* See {@link #registerListener(ChangeListener)} for more details.
*/
@@ -211,6 +222,7 @@
boolean hasPackageOverride(String pname) {
return mRawOverrides.containsKey(pname);
}
+
/**
* Remove any package override for the given package name, restoring the default behaviour.
*
@@ -355,7 +367,7 @@
override.setPackageName(entry.getKey());
override.setMinVersionCode(entry.getValue().getMinVersionCode());
override.setMaxVersionCode(entry.getValue().getMaxVersionCode());
- override.setEnabled(entry.getValue().getEnabled());
+ override.setEnabled(entry.getValue().isEnabled());
rawList.add(override);
}
changeOverrides.setRaw(rawOverrides);
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index ef86f42..55e2696 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -304,6 +304,16 @@
}
/**
+ * Returns whether the change is overridable.
+ */
+ boolean isOverridable(long changeId) {
+ synchronized (mChanges) {
+ CompatChange c = mChanges.get(changeId);
+ return c != null && c.getOverridable();
+ }
+ }
+
+ /**
* Removes an override previously added via {@link #addOverride(long, String, boolean)}.
*
* <p>This restores the default behaviour for the given change and app, once any app processes
@@ -343,7 +353,7 @@
/**
* Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or
- * {@link #addOverrides(CompatibilityChangeConfig, String)} for a certain package.
+ * {@link #addOverrides(CompatibilityOverrideConfig, String)} for a certain package.
*
* <p>This restores the default behaviour for the given app.
*
@@ -632,8 +642,11 @@
}
boolean shouldInvalidateCache = false;
for (CompatChange c: changes) {
+ if (!c.hasPackageOverride(packageName)) {
+ continue;
+ }
OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedState(c.getId(), packageName);
+ mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(), packageName);
shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext);
}
if (shouldInvalidateCache) {
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index aa66a1a..b500691 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -16,6 +16,9 @@
package com.android.server.compat;
+import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
@@ -24,6 +27,7 @@
import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -69,8 +73,25 @@
mForceNonDebuggableFinalBuild = false;
}
+ /**
+ * Check the allowed state for the given changeId and packageName on a recheck.
+ *
+ * <p>Recheck happens when the given app is getting updated. In this case we cannot do a
+ * permission check on the caller, so we're using the fact that the override was present as
+ * proof that the original caller was allowed to set this override.
+ */
+ OverrideAllowedState getOverrideAllowedStateForRecheck(long changeId,
+ @NonNull String packageName) {
+ return getOverrideAllowedStateInternal(changeId, packageName, true);
+ }
+
@Override
public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) {
+ return getOverrideAllowedStateInternal(changeId, packageName, false);
+ }
+
+ private OverrideAllowedState getOverrideAllowedStateInternal(long changeId, String packageName,
+ boolean isRecheck) {
if (mCompatConfig.isLoggingOnly(changeId)) {
return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1);
}
@@ -99,6 +120,16 @@
} catch (NameNotFoundException e) {
return new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1);
}
+ // If the change is annotated as @Overridable, apps with the specific permission can
+ // set the override even on production builds. When rechecking the override, e.g. during an
+ // app update we can bypass this check, as it wouldn't have been here in the first place.
+ if (mCompatConfig.isOverridable(changeId)
+ && (isRecheck
+ || mContext.checkCallingOrSelfPermission(
+ OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+ == PERMISSION_GRANTED)) {
+ return new OverrideAllowedState(ALLOWED, -1, -1);
+ }
int appTargetSdk = applicationInfo.targetSdkVersion;
// Only allow overriding debuggable apps.
if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
@@ -130,5 +161,4 @@
void forceNonDebuggableFinalForTest(boolean value) {
mForceNonDebuggableFinalBuild = value;
}
-
}
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 2be39aa..62de369 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.LOG_COMPAT_CHANGE;
import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
+import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD;
import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.SYSTEM_UID;
@@ -182,11 +183,12 @@
}
@Override
- public void setOverridesFromInstaller(CompatibilityOverrideConfig overrides,
+ public void setOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides,
String packageName) {
- checkCompatChangeOverridePermission();
+ // TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods.
+ checkCompatChangeOverrideOverridablePermission();
+ checkAllCompatOverridesAreOverridable(overrides);
mCompatConfig.addOverrides(overrides, packageName);
- killPackage(packageName);
}
@Override
@@ -383,6 +385,26 @@
}
}
+ private void checkCompatChangeOverrideOverridablePermission() {
+ // Don't check for permissions within the system process
+ if (Binder.getCallingUid() == SYSTEM_UID) {
+ return;
+ }
+ if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+ != PERMISSION_GRANTED) {
+ throw new SecurityException("Cannot override compat change");
+ }
+ }
+
+ private void checkAllCompatOverridesAreOverridable(CompatibilityOverrideConfig overrides) {
+ for (Long changeId : overrides.overrides.keySet()) {
+ if (!mCompatConfig.isOverridable(changeId)) {
+ throw new SecurityException("Only change ids marked as Overridable can be "
+ + "overridden.");
+ }
+ }
+ }
+
private void checkCompatChangeReadAndLogPermission() {
checkCompatChangeReadPermission();
checkCompatChangeLogPermission();
diff --git a/services/core/java/com/android/server/connectivity/FullScore.java b/services/core/java/com/android/server/connectivity/FullScore.java
index 9326d69..375d005 100644
--- a/services/core/java/com/android/server/connectivity/FullScore.java
+++ b/services/core/java/com/android/server/connectivity/FullScore.java
@@ -19,12 +19,14 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkScore.KEEP_CONNECTED_NONE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkScore;
+import android.net.NetworkScore.KeepConnectedReason;
import com.android.internal.annotations.VisibleForTesting;
@@ -95,9 +97,13 @@
// Bitmask of all the policies applied to this score.
private final long mPolicies;
- FullScore(final int legacyInt, final long policies) {
+ private final int mKeepConnectedReason;
+
+ FullScore(final int legacyInt, final long policies,
+ @KeepConnectedReason final int keepConnectedReason) {
mLegacyInt = legacyInt;
mPolicies = policies;
+ mKeepConnectedReason = keepConnectedReason;
}
/**
@@ -110,14 +116,15 @@
*/
public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
@NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config) {
- return withPolicies(score.getLegacyInt(), caps.hasCapability(NET_CAPABILITY_VALIDATED),
+ return withPolicies(score.getLegacyInt(), score.getKeepConnectedReason(),
+ caps.hasCapability(NET_CAPABILITY_VALIDATED),
caps.hasTransport(TRANSPORT_VPN),
config.explicitlySelected,
config.acceptUnvalidated);
}
/**
- * Given a score supplied by the NetworkAgent, produce a prospective score for an offer.
+ * Given a score supplied by a NetworkProvider, produce a prospective score for an offer.
*
* NetworkOffers have score filters that are compared to the scores of actual networks
* to see if they could possibly beat the current satisfier. Some things the agent can't
@@ -139,8 +146,8 @@
final boolean everUserSelected = false;
// Don't assume the user will accept unvalidated connectivity.
final boolean acceptUnvalidated = false;
- return withPolicies(score.getLegacyInt(), mayValidate, vpn, everUserSelected,
- acceptUnvalidated);
+ return withPolicies(score.getLegacyInt(), KEEP_CONNECTED_NONE,
+ mayValidate, vpn, everUserSelected, acceptUnvalidated);
}
/**
@@ -152,13 +159,15 @@
*/
public FullScore mixInScore(@NonNull final NetworkCapabilities caps,
@NonNull final NetworkAgentConfig config) {
- return withPolicies(mLegacyInt, caps.hasCapability(NET_CAPABILITY_VALIDATED),
+ return withPolicies(mLegacyInt, mKeepConnectedReason,
+ caps.hasCapability(NET_CAPABILITY_VALIDATED),
caps.hasTransport(TRANSPORT_VPN),
config.explicitlySelected,
config.acceptUnvalidated);
}
private static FullScore withPolicies(@NonNull final int legacyInt,
+ @KeepConnectedReason final int keepConnectedReason,
final boolean isValidated,
final boolean isVpn,
final boolean everUserSelected,
@@ -167,7 +176,8 @@
(isValidated ? 1L << POLICY_IS_VALIDATED : 0)
| (isVpn ? 1L << POLICY_IS_VPN : 0)
| (everUserSelected ? 1L << POLICY_EVER_USER_SELECTED : 0)
- | (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0));
+ | (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0),
+ keepConnectedReason);
}
/**
@@ -219,13 +229,21 @@
return 0 != (mPolicies & (1L << policy));
}
+ /**
+ * Returns the keep-connected reason, or KEEP_CONNECTED_NONE.
+ */
+ public int getKeepConnectedReason() {
+ return mKeepConnectedReason;
+ }
+
// Example output :
// Score(50 ; Policies : EVER_USER_SELECTED&IS_VALIDATED)
@Override
public String toString() {
final StringJoiner sj = new StringJoiner(
"&", // delimiter
- "Score(" + mLegacyInt + " ; Policies : ", // prefix
+ "Score(" + mLegacyInt + " ; KeepConnected : " + mKeepConnectedReason
+ + " ; Policies : ", // prefix
")"); // suffix
for (int i = NetworkScore.MIN_AGENT_MANAGED_POLICY;
i <= NetworkScore.MAX_AGENT_MANAGED_POLICY; ++i) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index a8cbcb5..d16a2de 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -201,6 +201,9 @@
// Set to true when partial connectivity was detected.
public boolean partialConnectivity;
+ // Delay between when the network is disconnected and when the native network is destroyed.
+ public int teardownDelayMs;
+
// Captive portal info of the network from RFC8908, if any.
// Obtained by ConnectivityService and merged into NetworkAgent-provided information.
public CaptivePortalData capportApiData;
@@ -577,6 +580,28 @@
}
}
+ /**
+ * Notify the NetworkAgent that the network is successfully connected.
+ */
+ public void onNetworkCreated() {
+ try {
+ networkAgent.onNetworkCreated();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending network created event", e);
+ }
+ }
+
+ /**
+ * Notify the NetworkAgent that the native network has been destroyed.
+ */
+ public void onNetworkDestroyed() {
+ try {
+ networkAgent.onNetworkDestroyed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending network destroyed event", e);
+ }
+ }
+
// TODO: consider moving out of NetworkAgentInfo into its own class
private class NetworkAgentMessageHandler extends INetworkAgentRegistry.Stub {
private final Handler mHandler;
@@ -653,6 +678,12 @@
@QosCallbackException.ExceptionType final int exceptionType) {
mQosCallbackTracker.sendEventQosCallbackError(qosCallbackId, exceptionType);
}
+
+ @Override
+ public void sendTeardownDelayMs(int teardownDelayMs) {
+ mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED,
+ teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
+ }
}
/**
@@ -862,6 +893,10 @@
return isWifi && !avoidBadWifi && everValidated;
}
+ public FullScore getScore() {
+ return mScore;
+ }
+
// Get the current score for this Network. This may be modified from what the
// NetworkAgent sent, as it has modifiers applied to it.
public int getCurrentScore() {
diff --git a/services/core/java/com/android/server/connectivity/NetworkOffer.java b/services/core/java/com/android/server/connectivity/NetworkOffer.java
index 548db6b..a0d3924 100644
--- a/services/core/java/com/android/server/connectivity/NetworkOffer.java
+++ b/services/core/java/com/android/server/connectivity/NetworkOffer.java
@@ -17,13 +17,14 @@
package com.android.server.connectivity;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.net.INetworkOfferCallback;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.os.RemoteException;
+import java.util.HashSet;
import java.util.Objects;
-
+import java.util.Set;
/**
* Represents an offer made by a NetworkProvider to create a network if a need arises.
@@ -44,46 +45,86 @@
@NonNull public final NetworkCapabilities caps;
@NonNull public final INetworkOfferCallback callback;
@NonNull public final int providerId;
+ // While this could, in principle, be deduced from the old values of the satisfying networks,
+ // doing so would add a lot of complexity and performance penalties. For each request, the
+ // ranker would have to run again to figure out if this offer used to be able to beat the
+ // previous satisfier to know if there is a change in whether this offer is now needed ;
+ // besides, there would be a need to handle an edge case when a new request comes online,
+ // where it's not satisfied before the first rematch, where starting to satisfy a request
+ // should not result in sending unneeded to this offer. This boolean, while requiring that
+ // the offers are only ever manipulated on the CS thread, is by far a simpler and
+ // economical solution.
+ private final Set<NetworkRequest> mCurrentlyNeeded = new HashSet<>();
- private static NetworkCapabilities emptyCaps() {
- final NetworkCapabilities nc = new NetworkCapabilities();
- return nc;
- }
-
- // Ideally the caps argument would be non-null, but null has historically meant no filter
- // and telephony passes null. Keep backward compatibility.
public NetworkOffer(@NonNull final FullScore score,
- @Nullable final NetworkCapabilities caps,
+ @NonNull final NetworkCapabilities caps,
@NonNull final INetworkOfferCallback callback,
@NonNull final int providerId) {
this.score = Objects.requireNonNull(score);
- this.caps = null != caps ? caps : emptyCaps();
+ this.caps = Objects.requireNonNull(caps);
this.callback = Objects.requireNonNull(callback);
this.providerId = providerId;
}
/**
+ * Tell the provider for this offer that the network is needed for a request.
+ * @param request the request for which the offer is needed
+ */
+ public void onNetworkNeeded(@NonNull final NetworkRequest request) {
+ if (mCurrentlyNeeded.contains(request)) {
+ throw new IllegalStateException("Network already needed");
+ }
+ mCurrentlyNeeded.add(request);
+ try {
+ callback.onNetworkNeeded(request);
+ } catch (final RemoteException e) {
+ // The provider is dead. It will be removed by the death recipient.
+ }
+ }
+
+ /**
+ * Tell the provider for this offer that the network is no longer needed for this request.
+ *
+ * onNetworkNeeded will have been called with the same request before.
+ *
+ * @param request the request
+ */
+ public void onNetworkUnneeded(@NonNull final NetworkRequest request) {
+ if (!mCurrentlyNeeded.contains(request)) {
+ throw new IllegalStateException("Network already unneeded");
+ }
+ mCurrentlyNeeded.remove(request);
+ try {
+ callback.onNetworkUnneeded(request);
+ } catch (final RemoteException e) {
+ // The provider is dead. It will be removed by the death recipient.
+ }
+ }
+
+ /**
+ * Returns whether this offer is currently needed for this request.
+ * @param request the request
+ * @return whether the offer is currently considered needed
+ */
+ public boolean neededFor(@NonNull final NetworkRequest request) {
+ return mCurrentlyNeeded.contains(request);
+ }
+
+ /**
* Migrate from, and take over, a previous offer.
*
* When an updated offer is sent from a provider, call this method on the new offer, passing
* the old one, to take over the state.
*
- * @param previousOffer
+ * @param previousOffer the previous offer
*/
public void migrateFrom(@NonNull final NetworkOffer previousOffer) {
if (!callback.equals(previousOffer.callback)) {
throw new IllegalArgumentException("Can only migrate from a previous version of"
+ " the same offer");
}
- }
-
- /**
- * Returns whether an offer can satisfy a NetworkRequest, according to its capabilities.
- * @param request The request to test against.
- * @return Whether this offer can satisfy the request.
- */
- public final boolean canSatisfy(@NonNull final NetworkRequest request) {
- return request.networkCapabilities.satisfiedByNetworkCapabilities(caps);
+ mCurrentlyNeeded.clear();
+ mCurrentlyNeeded.addAll(previousOffer.mCurrentlyNeeded);
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
index d0aabf95..0e4dda2 100644
--- a/services/core/java/com/android/server/connectivity/NetworkRanker.java
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import java.util.Collection;
@@ -47,4 +48,41 @@
}
return bestNetwork;
}
+
+ /**
+ * Returns whether an offer has a chance to beat a champion network for a request.
+ *
+ * Offers are sent by network providers when they think they might be able to make a network
+ * with the characteristics contained in the offer. If the offer has no chance to beat
+ * the currently best network for a given request, there is no point in the provider spending
+ * power trying to find and bring up such a network.
+ *
+ * Note that having an offer up does not constitute a commitment from the provider part
+ * to be able to bring up a network with these characteristics, or a network at all for
+ * that matter. This is only used to save power by letting providers know when they can't
+ * beat a current champion.
+ *
+ * @param request The request to evaluate against.
+ * @param championScore The currently best network for this request.
+ * @param offer The offer.
+ * @return Whether the offer stands a chance to beat the champion.
+ */
+ public boolean mightBeat(@NonNull final NetworkRequest request,
+ @Nullable final FullScore championScore,
+ @NonNull final NetworkOffer offer) {
+ // If this network can't even satisfy the request then it can't beat anything, not
+ // even an absence of network. It can't satisfy it anyway.
+ if (!request.canBeSatisfiedBy(offer.caps)) return false;
+ // If there is no satisfying network, then this network can beat, because some network
+ // is always better than no network.
+ if (null == championScore) return true;
+ final int offerIntScore;
+ if (offer.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ // If the offer might have Internet access, then it might validate.
+ offerIntScore = offer.score.getLegacyIntAsValidated();
+ } else {
+ offerIntScore = offer.score.getLegacyInt();
+ }
+ return championScore.getLegacyInt() < offerIntScore;
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a070f27..820198c 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -464,7 +464,7 @@
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
- .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE))
+ .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE, null))
.build();
loadAlwaysOnPackage();
@@ -528,7 +528,7 @@
private void resetNetworkCapabilities() {
mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
.setUids(null)
- .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE))
+ .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE, null))
.build();
}
@@ -1259,7 +1259,7 @@
capsBuilder.setUids(createUserAndRestrictedProfilesRanges(mUserId,
mConfig.allowedApplications, mConfig.disallowedApplications));
- capsBuilder.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+ capsBuilder.setTransportInfo(new VpnTransportInfo(getActiveVpnType(), mConfig.session));
// Only apps targeting Q and above can explicitly declare themselves as metered.
// These VPNs are assumed metered unless they state otherwise.
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
new file mode 100644
index 0000000..8ce7b66
--- /dev/null
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.display;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.Display;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Saves brightness to a persistent data store, enabling each logical display to have its own
+ * brightness.
+ */
+public class BrightnessSetting {
+ private static final String TAG = "BrightnessSetting";
+
+ private static final int MSG_BRIGHTNESS_CHANGED = 1;
+ private static final Uri BRIGHTNESS_FLOAT_URI =
+ Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
+ private final PersistentDataStore mPersistentDataStore;
+
+ private final boolean mIsDefaultDisplay;
+ private final Context mContext;
+ private final LogicalDisplay mLogicalDisplay;
+ private final Object mLock = new Object();
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_BRIGHTNESS_CHANGED) {
+ float brightnessVal = Float.intBitsToFloat(msg.arg1);
+ notifyListeners(brightnessVal);
+ }
+ }
+ };
+
+ private final ContentObserver mBrightnessSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (selfChange) {
+ return;
+ }
+ if (BRIGHTNESS_FLOAT_URI.equals(uri)) {
+ float brightness = getScreenBrightnessSettingFloat();
+ setBrightness(brightness, true);
+ }
+ }
+ };
+
+ private final CopyOnWriteArrayList<BrightnessSettingListener> mListeners =
+ new CopyOnWriteArrayList<BrightnessSettingListener>();
+
+ private float mBrightness;
+
+ BrightnessSetting(@NonNull PersistentDataStore persistentDataStore,
+ @NonNull LogicalDisplay logicalDisplay,
+ @NonNull Context context) {
+ mPersistentDataStore = persistentDataStore;
+ mLogicalDisplay = logicalDisplay;
+ mContext = context;
+ mIsDefaultDisplay = mLogicalDisplay.getDisplayIdLocked() == Display.DEFAULT_DISPLAY;
+ mBrightness = mPersistentDataStore.getBrightness(
+ mLogicalDisplay.getPrimaryDisplayDeviceLocked());
+ if (mIsDefaultDisplay) {
+ mContext.getContentResolver().registerContentObserver(BRIGHTNESS_FLOAT_URI,
+ false, mBrightnessSettingsObserver);
+ }
+ }
+
+ /**
+ * Returns the brightness from the brightness setting
+ *
+ * @return brightness for the current display
+ */
+ public float getBrightness() {
+ return mBrightness;
+ }
+
+ /**
+ * Registers listener for brightness setting change events.
+ */
+ public void registerListener(BrightnessSettingListener l) {
+ if (!mListeners.contains(l)) {
+ mListeners.add(l);
+ }
+ }
+
+ /**
+ * Unregisters listener for brightness setting change events.
+ *
+ * @param l listener
+ */
+ public void unregisterListener(BrightnessSettingListener l) {
+ mListeners.remove(l);
+ }
+
+ void setBrightness(float brightness) {
+ setBrightness(brightness, false);
+ }
+
+ private void setBrightness(float brightness, boolean isFromSystemSetting) {
+ if (brightness == mBrightness) {
+ return;
+ }
+ if (Float.isNaN(brightness)) {
+ Slog.w(TAG, "Attempting to set invalid brightness");
+ return;
+ }
+ synchronized (mLock) {
+
+ mBrightness = brightness;
+
+ // If it didn't come from us
+ if (mIsDefaultDisplay && !isFromSystemSetting) {
+ Settings.System.putFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightness,
+ UserHandle.USER_CURRENT);
+ }
+ mPersistentDataStore.setBrightness(mLogicalDisplay.getPrimaryDisplayDeviceLocked(),
+ brightness);
+ int toSend = Float.floatToIntBits(mBrightness);
+ Message msg = mHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED, toSend, 0);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ private float getScreenBrightnessSettingFloat() {
+ return Settings.System.getFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FLOAT, PowerManager.BRIGHTNESS_INVALID_FLOAT,
+ UserHandle.USER_CURRENT);
+ }
+
+ private void notifyListeners(float brightness) {
+ for (BrightnessSettingListener l : mListeners) {
+ l.onBrightnessChanged(brightness);
+ }
+ }
+
+ /**
+ * Listener for changes to system brightness.
+ */
+ public interface BrightnessSettingListener {
+
+ /**
+ * Notify that the brightness has changed.
+ */
+ void onBrightnessChanged(float brightness);
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c010906..393a4eb 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -203,6 +203,8 @@
private WindowManagerInternal mWindowManagerInternal;
private InputManagerInternal mInputManagerInternal;
private IMediaProjectionManager mProjectionService;
+ private int[] mUserDisabledHdrTypes = {};
+ private boolean mAreUserDisabledHdrTypesAllowed = true;
// The synchronization root for the display manager.
// This lock guards most of the display manager's state.
@@ -557,6 +559,8 @@
recordTopInsetLocked(mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY));
updateSettingsLocked();
+
+ updateUserDisabledHdrTypesFromSettingsLocked();
}
mDisplayModeDirector.setDesiredDisplayModeSpecsListener(
@@ -566,6 +570,7 @@
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
mSettingsObserver = new SettingsObserver();
+
mBrightnessSynchronizer.startSynchronizing();
}
@@ -737,6 +742,42 @@
Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED, 1, UserHandle.USER_CURRENT) != 0;
}
+ private void updateUserDisabledHdrTypesFromSettingsLocked() {
+ mAreUserDisabledHdrTypesAllowed = (Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED,
+ 1) != 0);
+
+ String userDisabledHdrTypes = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.USER_DISABLED_HDR_FORMATS);
+
+ if (userDisabledHdrTypes != null) {
+ try {
+ String[] userDisabledHdrTypeStrings =
+ TextUtils.split(userDisabledHdrTypes, ",");
+ mUserDisabledHdrTypes = new int[userDisabledHdrTypeStrings.length];
+ for (int i = 0; i < userDisabledHdrTypeStrings.length; i++) {
+ mUserDisabledHdrTypes[i] = Integer.parseInt(userDisabledHdrTypeStrings[i]);
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG,
+ "Failed to parse USER_DISABLED_HDR_FORMATS. "
+ + "Clearing the setting.", e);
+ clearUserDisabledHdrTypesLocked();
+ }
+ } else {
+ clearUserDisabledHdrTypesLocked();
+ }
+ }
+
+ private void clearUserDisabledHdrTypesLocked() {
+ mUserDisabledHdrTypes = new int[]{};
+ synchronized (mSyncRoot) {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.USER_DISABLED_HDR_FORMATS, "");
+ }
+ }
+
private DisplayInfo getDisplayInfoForFrameRateOverride(DisplayEventReceiver.FrameRateOverride[]
frameRateOverrides, DisplayInfo info, int callingUid) {
float frameRateHz = 0;
@@ -957,6 +998,61 @@
}
}
+ private void setUserDisabledHdrTypesInternal(int[] userDisabledHdrTypes) {
+ synchronized (mSyncRoot) {
+ if (userDisabledHdrTypes == null) {
+ Slog.e(TAG, "Null is not an expected argument to "
+ + "setUserDisabledHdrTypesInternal");
+ return;
+ }
+ Arrays.sort(userDisabledHdrTypes);
+ if (Arrays.equals(mUserDisabledHdrTypes, userDisabledHdrTypes)) {
+ return;
+ }
+ String userDisabledFormatsString = "";
+ if (userDisabledHdrTypes.length != 0) {
+ userDisabledFormatsString = TextUtils.join(",",
+ Arrays.stream(userDisabledHdrTypes).boxed().toArray());
+ }
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.USER_DISABLED_HDR_FORMATS, userDisabledFormatsString);
+ mUserDisabledHdrTypes = userDisabledHdrTypes;
+ if (!mAreUserDisabledHdrTypesAllowed) {
+ mLogicalDisplayMapper.forEachLocked(
+ display -> {
+ display.setUserDisabledHdrTypes(userDisabledHdrTypes);
+ handleLogicalDisplayChangedLocked(display);
+ });
+ }
+ }
+ }
+
+ private void setAreUserDisabledHdrTypesAllowedInternal(
+ boolean areUserDisabledHdrTypesAllowed) {
+ synchronized (mSyncRoot) {
+ if (mAreUserDisabledHdrTypesAllowed == areUserDisabledHdrTypesAllowed) {
+ return;
+ }
+ mAreUserDisabledHdrTypesAllowed = areUserDisabledHdrTypesAllowed;
+ if (mUserDisabledHdrTypes.length == 0) {
+ return;
+ }
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED,
+ areUserDisabledHdrTypesAllowed ? 1 : 0);
+ int userDisabledHdrTypes[] = {};
+ if (!mAreUserDisabledHdrTypesAllowed) {
+ userDisabledHdrTypes = mUserDisabledHdrTypes;
+ }
+ int[] finalUserDisabledHdrTypes = userDisabledHdrTypes;
+ mLogicalDisplayMapper.forEachLocked(
+ display -> {
+ display.setUserDisabledHdrTypes(finalUserDisabledHdrTypes);
+ handleLogicalDisplayChangedLocked(display);
+ });
+ }
+ }
+
private void requestColorModeInternal(int displayId, int colorMode) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
@@ -1130,17 +1226,16 @@
final int displayId = display.getDisplayIdLocked();
final boolean isDefault = displayId == Display.DEFAULT_DISPLAY;
configureColorModeLocked(display, device);
+ if (!mAreUserDisabledHdrTypesAllowed) {
+ display.setUserDisabledHdrTypes(mUserDisabledHdrTypes);
+ }
if (isDefault) {
recordStableDisplayStatsIfNeededLocked(display);
recordTopInsetLocked(display);
}
- final int groupId = mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(displayId);
- if (groupId != Display.INVALID_DISPLAY_GROUP) {
- addDisplayPowerControllerLocked(display);
- mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
- } else {
- mDisplayStates.append(displayId, Display.STATE_ON);
- }
+ addDisplayPowerControllerLocked(display);
+ mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
+
mDisplayBrightnesses.append(displayId, display.getDisplayInfoLocked().brightnessDefault);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
@@ -1814,6 +1909,14 @@
pw.println(" mMinimumBrightnessCurve=" + mMinimumBrightnessCurve);
pw.println();
+ if (!mAreUserDisabledHdrTypesAllowed) {
+ pw.println(" mUserDisabledHdrTypes: size=" + mUserDisabledHdrTypes.length);
+ for (int type : mUserDisabledHdrTypes) {
+ pw.println(" " + type);
+ }
+ }
+
+ pw.println();
final int displayStateCount = mDisplayStates.size();
pw.println("Display States: size=" + displayStateCount);
for (int i = 0; i < displayStateCount; i++) {
@@ -1943,8 +2046,7 @@
}
private void initializeDisplayPowerControllersLocked() {
- mLogicalDisplayMapper.forEachLocked((logicalDisplay) -> addDisplayPowerControllerLocked(
- logicalDisplay));
+ mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked);
}
private void addDisplayPowerControllerLocked(LogicalDisplay display) {
@@ -1955,9 +2057,12 @@
if (mBrightnessTracker == null) {
mBrightnessTracker = new BrightnessTracker(mContext, null);
}
+
+ final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
+ display, mContext);
final DisplayPowerController displayPowerController = new DisplayPowerController(
mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
- mDisplayBlanker, display, mBrightnessTracker);
+ mDisplayBlanker, display, mBrightnessTracker, brightnessSetting);
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
@@ -2339,6 +2444,45 @@
}
@Override // Binder call
+ public void setUserDisabledHdrTypes(int[] userDisabledFormats) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.WRITE_SECURE_SETTINGS,
+ "Permission required to write the user settings.");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setUserDisabledHdrTypesInternal(userDisabledFormats);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.WRITE_SECURE_SETTINGS,
+ "Permission required to write the user settings.");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setAreUserDisabledHdrTypesAllowedInternal(areUserDisabledHdrTypesAllowed);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public boolean areUserDisabledHdrTypesAllowed() {
+ synchronized (mSyncRoot) {
+ return mAreUserDisabledHdrTypesAllowed;
+ }
+ }
+
+ @Override // Binder call
+ public int[] getUserDisabledHdrTypes() {
+ return mUserDisabledHdrTypes;
+ }
+
+ @Override // Binder call
public void requestColorMode(int displayId, int colorMode) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE,
@@ -2662,6 +2806,48 @@
}
@Override // Binder call
+ public void setBrightness(int displayId, float brightness) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
+ "Permission required to set the display's brightness");
+ if (!isValidBrightness(brightness)) {
+ Slog.w(TAG, "Attempted to set invalid brightness" + brightness);
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ if (dpc != null) {
+ dpc.putScreenBrightnessSetting(brightness);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public float getBrightness(int displayId) {
+ float brightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
+ "Permission required to set the display's brightness");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ if (dpc != null) {
+ brightness = dpc.getScreenBrightnessSetting();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return brightness;
+ }
+
+ @Override // Binder call
public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
@@ -2746,9 +2932,6 @@
@Override // Binder call
public int getRefreshRateSwitchingType() {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
- "Permission required read refresh rate switching type.");
final long token = Binder.clearCallingIdentity();
try {
return getRefreshRateSwitchingTypeInternal();
@@ -2809,6 +2992,13 @@
Slog.w(TAG, msg);
return false;
}
+
+ }
+
+ private static boolean isValidBrightness(float brightness) {
+ return !Float.isNaN(brightness)
+ && (brightness >= PowerManager.BRIGHTNESS_MIN)
+ && (brightness <= PowerManager.BRIGHTNESS_MAX);
}
private final class LocalService extends DisplayManagerInternal {
@@ -2839,10 +3029,16 @@
final int size = displayGroup.getSizeLocked();
boolean ready = true;
for (int i = 0; i < size; i++) {
- final DisplayPowerController displayPowerController =
- mDisplayPowerControllers.get(displayGroup.getIdLocked(i));
- ready &= displayPowerController.requestPowerState(request,
- waitForNegativeProximity);
+ final int id = displayGroup.getIdLocked(i);
+ final DisplayDevice displayDevice = mLogicalDisplayMapper.getDisplayLocked(
+ id).getPrimaryDisplayDeviceLocked();
+ final int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
+ if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
+ final DisplayPowerController displayPowerController =
+ mDisplayPowerControllers.get(id);
+ ready &= displayPowerController.requestPowerState(request,
+ waitForNegativeProximity);
+ }
}
return ready;
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index d1d0496..48edb73 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -16,13 +16,11 @@
package com.android.server.display;
-import android.Manifest;
import android.content.Context;
import android.content.Intent;
-import android.os.Binder;
+import android.hardware.display.DisplayManager;
import android.os.ShellCommand;
-import android.os.UserHandle;
-import android.provider.Settings;
+import android.view.Display;
import java.io.PrintWriter;
@@ -111,17 +109,8 @@
}
final Context context = mService.getContext();
- context.enforceCallingOrSelfPermission(
- Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
- "Permission required to set the display's brightness");
- final long token = Binder.clearCallingIdentity();
- try {
- Settings.System.putFloatForUser(context.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightness,
- UserHandle.USER_CURRENT);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ dm.setBrightness(Display.DEFAULT_DISPLAY, brightness);
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 645ca7a..4bbd338 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -263,6 +263,8 @@
highestConsideredPriority = Vote.PRIORITY_APP_REQUEST_SIZE;
}
+ // We try to find a range of priorities which define a non-empty set of allowed display
+ // modes. Each time we fail we increase the lowest priority.
while (lowestConsideredPriority <= highestConsideredPriority) {
summarizeVotes(
votes, lowestConsideredPriority, highestConsideredPriority, primarySummary);
@@ -343,8 +345,15 @@
}
if (baseModeId == INVALID_DISPLAY_MODE_ID) {
- throw new IllegalStateException("Can't select a base display mode for display "
- + displayId + ". The votes are " + mVotesByDisplay.valueAt(displayId));
+ Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling"
+ + " back to the default mode. Display = " + displayId + ", votes = " + votes
+ + ", supported modes = " + Arrays.toString(modes));
+
+ float fps = defaultMode.getRefreshRate();
+ return new DesiredDisplayModeSpecs(defaultMode.getModeId(),
+ /*allowGroupSwitching */ false,
+ new RefreshRateRange(fps, fps),
+ new RefreshRateRange(fps, fps));
}
if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 7110d3e..cb8541e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -122,6 +122,7 @@
private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7;
private static final int MSG_IGNORE_PROXIMITY = 8;
private static final int MSG_STOP = 9;
+ private static final int MSG_UPDATE_BRIGHTNESS = 10;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -355,13 +356,14 @@
private final HighBrightnessModeController mHbmController;
+ private final BrightnessSetting mBrightnessSetting;
+
// A record of state for skipping brightness ramps.
private int mSkipRampState = RAMP_STATE_SKIP_NONE;
// The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL.
private float mInitialAutoBrightness;
-
// The controller for the automatic brightness level.
private AutomaticBrightnessController mAutomaticBrightnessController;
@@ -410,6 +412,7 @@
private ObjectAnimator mColorFadeOnAnimator;
private ObjectAnimator mColorFadeOffAnimator;
private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
+ private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener;
// True if this DisplayPowerController has been stopped and should no longer be running.
private boolean mStopped;
@@ -420,7 +423,7 @@
public DisplayPowerController(Context context,
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
- BrightnessTracker brightnessTracker) {
+ BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting) {
mLogicalDisplay = logicalDisplay;
mDisplayId = mLogicalDisplay.getDisplayIdLocked();
mHandler = new DisplayControllerHandler(handler.getLooper());
@@ -439,7 +442,7 @@
mContext = context;
mBrightnessTracker = brightnessTracker;
-
+ mBrightnessSetting = brightnessSetting;
PowerManager pm = context.getSystemService(PowerManager.class);
final Resources resources = context.getResources();
@@ -785,6 +788,10 @@
mAutomaticBrightnessController.stop();
}
+ if (mBrightnessSetting != null) {
+ mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
+ }
+
mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
}
}
@@ -831,10 +838,12 @@
if (brightness >= PowerManager.BRIGHTNESS_MIN) {
mBrightnessTracker.start(brightness);
}
+ mBrightnessSettingListener = brightnessValue -> {
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue);
+ mHandler.sendMessage(msg);
+ };
- mContext.getContentResolver().registerContentObserver(
- Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT),
- false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
+ mBrightnessSetting.registerListener(mBrightnessSettingListener);
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT),
false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
@@ -865,6 +874,10 @@
private void cleanupHandlerThreadAfterStop() {
setProximitySensorEnabled(false);
mHandler.removeCallbacksAndMessages(null);
+ if (mUnfinishedBusiness) {
+ mCallbacks.releaseSuspendBlocker();
+ mUnfinishedBusiness = false;
+ }
if (mPowerState != null) {
mPowerState.stop();
mPowerState = null;
@@ -1150,7 +1163,7 @@
// before applying the low power or dim transformations so that the slider
// accurately represents the full possible range, even if they range changes what
// it means in absolute terms.
- putScreenBrightnessSetting(brightnessState);
+ putScreenBrightnessSetting(brightnessState, /* updateCurrent */ true);
}
// Apply dimming by at least some minimum amount when user activity
@@ -1692,12 +1705,7 @@
}
}
- private final Runnable mCleanListener = new Runnable() {
- @Override
- public void run() {
- sendUpdatePowerState();
- }
- };
+ private final Runnable mCleanListener = this::sendUpdatePowerState;
private void setProximitySensorEnabled(boolean enable) {
if (enable) {
@@ -1804,7 +1812,6 @@
private void handleSettingsChange(boolean userSwitch) {
mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
-
if (userSwitch) {
// Don't treat user switches as user initiated change.
mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
@@ -1825,10 +1832,11 @@
return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
}
- private float getScreenBrightnessSetting() {
- final float brightness = Settings.System.getFloatForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_FLOAT, mScreenBrightnessDefault,
- UserHandle.USER_CURRENT);
+ float getScreenBrightnessSetting() {
+ float brightness = mBrightnessSetting.getBrightness();
+ if (Float.isNaN(brightness)) {
+ brightness = mScreenBrightnessDefault;
+ }
return clampAbsoluteBrightness(brightness);
}
@@ -1839,13 +1847,15 @@
return clampScreenBrightnessForVr(brightnessFloat);
}
- private void putScreenBrightnessSetting(float brightnessValue) {
- if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ void putScreenBrightnessSetting(float brightnessValue) {
+ putScreenBrightnessSetting(brightnessValue, false);
+ }
+
+ private void putScreenBrightnessSetting(float brightnessValue, boolean updateCurrent) {
+ if (updateCurrent) {
mCurrentScreenBrightnessSetting = brightnessValue;
- Settings.System.putFloatForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessValue,
- UserHandle.USER_CURRENT);
}
+ mBrightnessSetting.setBrightness(brightnessValue);
}
private void putAutoBrightnessAdjustmentSetting(float adjustment) {
@@ -2175,7 +2185,7 @@
}
break;
case MSG_CONFIGURE_BRIGHTNESS:
- mBrightnessConfiguration = (BrightnessConfiguration)msg.obj;
+ mBrightnessConfiguration = (BrightnessConfiguration) msg.obj;
updatePowerState();
break;
@@ -2197,6 +2207,12 @@
case MSG_STOP:
cleanupHandlerThreadAfterStop();
break;
+
+ case MSG_UPDATE_BRIGHTNESS:
+ if (mStopped) {
+ return;
+ }
+ handleSettingsChange(false /*userSwitch*/);
}
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index d9570c7..1589419 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -102,6 +102,8 @@
private int mRequestedColorMode;
private boolean mRequestedMinimalPostProcessing;
+ private int[] mUserDisabledHdrTypes = {};
+
private DisplayModeDirector.DesiredDisplayModeSpecs mDesiredDisplayModeSpecs =
new DisplayModeDirector.DesiredDisplayModeSpecs();
@@ -367,6 +369,7 @@
deviceInfo.supportedColorModes,
deviceInfo.supportedColorModes.length);
mBaseDisplayInfo.hdrCapabilities = deviceInfo.hdrCapabilities;
+ mBaseDisplayInfo.userDisabledHdrTypes = mUserDisabledHdrTypes;
mBaseDisplayInfo.minimalPostProcessingSupported =
deviceInfo.allmSupported || deviceInfo.gameContentTypeSupported;
mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
@@ -680,6 +683,14 @@
mDisplayScalingDisabled = disableScaling;
}
+ public void setUserDisabledHdrTypes(@NonNull int[] userDisabledHdrTypes) {
+ if (mUserDisabledHdrTypes != userDisabledHdrTypes) {
+ mUserDisabledHdrTypes = userDisabledHdrTypes;
+ mBaseDisplayInfo.userDisabledHdrTypes = userDisabledHdrTypes;
+ mInfo.set(null);
+ }
+ }
+
/**
* Swap the underlying {@link DisplayDevice} with the specified LogicalDisplay.
*
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 6c2e6eb..fcfa674 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -433,37 +433,31 @@
final int displayId = display.getDisplayIdLocked();
// Get current display group data
- final int groupId = getDisplayGroupIdFromDisplayIdLocked(displayId);
+ int groupId = getDisplayGroupIdFromDisplayIdLocked(displayId);
final DisplayGroup oldGroup = getDisplayGroupLocked(groupId);
// Get the new display group if a change is needed
final DisplayInfo info = display.getDisplayInfoLocked();
final boolean needsOwnDisplayGroup = (info.flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0;
final boolean hasOwnDisplayGroup = groupId != Display.DEFAULT_DISPLAY_GROUP;
- final boolean needsDisplayGroup = needsOwnDisplayGroup || info.type == Display.TYPE_INTERNAL
- || info.type == Display.TYPE_EXTERNAL;
- if (!needsDisplayGroup) {
- if (oldGroup != null) {
- oldGroup.removeDisplayLocked(display);
- }
- return;
- }
if (groupId == Display.INVALID_DISPLAY_GROUP
|| hasOwnDisplayGroup != needsOwnDisplayGroup) {
+ groupId = assignDisplayGroupIdLocked(needsOwnDisplayGroup);
+ }
+
+ // Create a new group if needed
+ DisplayGroup newGroup = getDisplayGroupLocked(groupId);
+ if (newGroup == null) {
+ newGroup = new DisplayGroup(groupId);
+ mDisplayGroups.append(groupId, newGroup);
+ }
+ if (oldGroup != newGroup) {
if (oldGroup != null) {
oldGroup.removeDisplayLocked(display);
}
-
- final int newGroupId = assignDisplayGroupIdLocked(needsOwnDisplayGroup);
- // Create a new group if needed
- DisplayGroup newGroup = getDisplayGroupLocked(newGroupId);
- if (newGroup == null) {
- newGroup = new DisplayGroup(newGroupId);
- mDisplayGroups.append(newGroupId, newGroup);
- }
newGroup.addDisplayLocked(display);
- display.updateDisplayGroupIdLocked(newGroupId);
- Slog.i(TAG, "Setting new display group " + newGroupId + " for display "
+ display.updateDisplayGroupIdLocked(groupId);
+ Slog.i(TAG, "Setting new display group " + groupId + " for display "
+ displayId + ", from previous group: "
+ (oldGroup != null ? oldGroup.getGroupId() : "null"));
}
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index a62642b..c90ddf4 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -62,6 +62,7 @@
* <display-states>
* <display unique-id="XXXXXXX">
* <color-mode>0</color-mode>
+ * <brightness-value>0</brightness-value>
* </display>
* </display-states>
* <stable-device-values>
@@ -82,7 +83,7 @@
* TODO: refactor this to extract common code shared with the input manager's data store
*/
final class PersistentDataStore {
- static final String TAG = "DisplayManager";
+ static final String TAG = "DisplayManager.PersistentDataStore";
private static final String TAG_DISPLAY_MANAGER_STATE = "display-manager-state";
@@ -95,6 +96,7 @@
private static final String TAG_DISPLAY_STATES = "display-states";
private static final String TAG_DISPLAY = "display";
private static final String TAG_COLOR_MODE = "color-mode";
+ private static final String TAG_BRIGHTNESS_VALUE = "brightness-value";
private static final String ATTR_UNIQUE_ID = "unique-id";
private static final String TAG_STABLE_DEVICE_VALUES = "stable-device-values";
@@ -255,6 +257,30 @@
return false;
}
+ public float getBrightness(DisplayDevice device) {
+ if (device == null || !device.hasStableUniqueId()) {
+ return Float.NaN;
+ }
+ final DisplayState state = getDisplayState(device.getUniqueId(), false);
+ if (state == null) {
+ return Float.NaN;
+ }
+ return state.getBrightness();
+ }
+
+ public boolean setBrightness(DisplayDevice displayDevice, float brightness) {
+ final String displayDeviceUniqueId = displayDevice.getUniqueId();
+ if (!displayDevice.hasStableUniqueId() || displayDeviceUniqueId == null) {
+ return false;
+ }
+ final DisplayState state = getDisplayState(displayDeviceUniqueId, true);
+ if (state.setBrightness(brightness)) {
+ setDirty();
+ return true;
+ }
+ return false;
+ }
+
public Point getStableDisplaySize() {
loadIfNeeded();
return mStableDeviceValues.getDisplaySize();
@@ -473,6 +499,7 @@
private static final class DisplayState {
private int mColorMode;
+ private float mBrightness;
public boolean setColorMode(int colorMode) {
if (colorMode == mColorMode) {
@@ -486,14 +513,33 @@
return mColorMode;
}
+ public boolean setBrightness(float brightness) {
+ if (brightness == mBrightness) {
+ return false;
+ }
+ mBrightness = brightness;
+ return true;
+ }
+
+ public float getBrightness() {
+ return mBrightness;
+ }
+
+
public void loadFromXml(TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (parser.getName().equals(TAG_COLOR_MODE)) {
- String value = parser.nextText();
- mColorMode = Integer.parseInt(value);
+ switch (parser.getName()) {
+ case TAG_COLOR_MODE:
+ String value = parser.nextText();
+ mColorMode = Integer.parseInt(value);
+ break;
+ case TAG_BRIGHTNESS_VALUE:
+ String brightness = parser.nextText();
+ mBrightness = Float.parseFloat(brightness);
+ break;
}
}
}
@@ -502,10 +548,15 @@
serializer.startTag(null, TAG_COLOR_MODE);
serializer.text(Integer.toString(mColorMode));
serializer.endTag(null, TAG_COLOR_MODE);
+ serializer.startTag(null, TAG_BRIGHTNESS_VALUE);
+ serializer.text(Float.toString(mBrightness));
+ serializer.endTag(null, TAG_BRIGHTNESS_VALUE);
+
}
public void dump(final PrintWriter pw, final String prefix) {
pw.println(prefix + "ColorMode=" + mColorMode);
+ pw.println(prefix + "BrightnessValue=" + mBrightness);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index cd66a8f..03fb3a4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -556,8 +556,10 @@
}
}
- addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress,
- activeSource.physicalAddress, deviceType));
+ if (!mService.isPowerStandbyOrTransient()) {
+ addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress,
+ activeSource.physicalAddress, deviceType));
+ }
}
private boolean handleNewDeviceAtTheTailOfActivePath(int path) {
@@ -709,10 +711,12 @@
@ServiceThreadOnly
void onNewAvrAdded(HdmiDeviceInfo avr) {
assertRunOnServiceThread();
- addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
- if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
- && !hasAction(SetArcTransmissionStateAction.class)) {
- startArcAction(true);
+ if (!mService.isPowerStandbyOrTransient()) {
+ addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
+ if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
+ && !hasAction(SetArcTransmissionStateAction.class)) {
+ startArcAction(true);
+ }
}
}
@@ -1263,6 +1267,7 @@
// Remove recording actions.
removeAction(OneTouchRecordAction.class);
removeAction(TimerRecordingAction.class);
+ removeAction(NewDeviceAction.class);
disableSystemAudioIfExist();
disableArcIfExist();
@@ -1303,12 +1308,20 @@
setArcStatus(false);
// Seq #44.
- removeAction(RequestArcInitiationAction.class);
+ removeAllRunningArcAction();
if (!hasAction(RequestArcTerminationAction.class) && isArcEstablished()) {
addAndStartAction(new RequestArcTerminationAction(this, avr.getLogicalAddress()));
}
}
+ @ServiceThreadOnly
+ private void removeAllRunningArcAction() {
+ // Running or pending actions make TV fail to broadcast <Standby> to connected devices
+ removeAction(RequestArcTerminationAction.class);
+ removeAction(RequestArcInitiationAction.class);
+ removeAction(SetArcTransmissionStateAction.class);
+ }
+
@Override
@ServiceThreadOnly
protected void onStandby(boolean initiatedByCec, int standbyAction) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c9364c6..27f8fd3 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4830,6 +4830,9 @@
setInputMethodEnabledLocked(defaultImiId, true);
}
}
+
+ updateDefaultVoiceImeIfNeededLocked();
+
// Here is not the perfect place to reset the switching controller. Ideally
// mSwitchingController and mSettings should be able to share the same state.
// TODO: Make sure that mSwitchingController and mSettings are sharing the
@@ -4842,6 +4845,37 @@
mSettings.getCurrentUserId(), 0 /* unused */, inputMethodList).sendToTarget();
}
+ @GuardedBy("mMethodMap")
+ private void updateDefaultVoiceImeIfNeededLocked() {
+ final String systemSpeechRecognizer =
+ mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer);
+ final String currentDefaultVoiceImeId = mSettings.getDefaultVoiceInputMethod();
+ final InputMethodInfo newSystemVoiceIme = InputMethodUtils.chooseSystemVoiceIme(
+ mMethodMap, systemSpeechRecognizer, currentDefaultVoiceImeId);
+ if (newSystemVoiceIme == null) {
+ if (DEBUG) {
+ Slog.i(TAG, "Found no valid default Voice IME. If the user is still locked,"
+ + " this may be expected.");
+ }
+ // Clear DEFAULT_VOICE_INPUT_METHOD when necessary. Note that InputMethodSettings
+ // does not update the actual Secure Settings until the user is unlocked.
+ if (!TextUtils.isEmpty(currentDefaultVoiceImeId)) {
+ mSettings.putDefaultVoiceInputMethod("");
+ // We don't support disabling the voice ime when a package is removed from the
+ // config.
+ }
+ return;
+ }
+ if (TextUtils.equals(currentDefaultVoiceImeId, newSystemVoiceIme.getId())) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.i(TAG, "Enabling the default Voice IME:" + newSystemVoiceIme);
+ }
+ setInputMethodEnabledLocked(newSystemVoiceIme.getId(), true);
+ mSettings.putDefaultVoiceInputMethod(newSystemVoiceIme.getId());
+ }
+
// ----------------------------------------------------------------------
private void showInputMethodAndSubtypeEnabler(String inputMethodId) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 0e908d4..ac3c31d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -337,6 +337,52 @@
return getDefaultEnabledImes(context, imis, false /* onlyMinimum */);
}
+ /**
+ * Chooses an eligible system voice IME from the given IMEs.
+ *
+ * @param methodMap Map from the IME ID to {@link InputMethodInfo}.
+ * @param systemSpeechRecognizerPackageName System speech recognizer configured by the system
+ * config.
+ * @param currentDefaultVoiceImeId IME ID currently set to
+ * {@link Settings.Secure#DEFAULT_VOICE_INPUT_METHOD}
+ * @return {@link InputMethodInfo} that is found in {@code methodMap} and most suitable for
+ * the system voice IME.
+ */
+ @Nullable
+ static InputMethodInfo chooseSystemVoiceIme(
+ @NonNull ArrayMap<String, InputMethodInfo> methodMap,
+ @Nullable String systemSpeechRecognizerPackageName,
+ @Nullable String currentDefaultVoiceImeId) {
+ if (TextUtils.isEmpty(systemSpeechRecognizerPackageName)) {
+ return null;
+ }
+ final InputMethodInfo defaultVoiceIme = methodMap.get(currentDefaultVoiceImeId);
+ // If the config matches the package of the setting, use the current one.
+ if (defaultVoiceIme != null && defaultVoiceIme.isSystem()
+ && defaultVoiceIme.getPackageName().equals(systemSpeechRecognizerPackageName)) {
+ return defaultVoiceIme;
+ }
+ InputMethodInfo firstMatchingIme = null;
+ final int methodCount = methodMap.size();
+ for (int i = 0; i < methodCount; ++i) {
+ final InputMethodInfo imi = methodMap.valueAt(i);
+ if (!imi.isSystem()) {
+ continue;
+ }
+ if (!TextUtils.equals(imi.getPackageName(), systemSpeechRecognizerPackageName)) {
+ continue;
+ }
+ if (firstMatchingIme != null) {
+ Slog.e(TAG, "At most one InputMethodService can be published in "
+ + "systemSpeechRecognizer: " + systemSpeechRecognizerPackageName
+ + ". Ignoring all of them.");
+ return null;
+ }
+ firstMatchingIme = imi;
+ }
+ return firstMatchingIme;
+ }
+
static boolean containsSubtypeOf(InputMethodInfo imi, @Nullable Locale locale,
boolean checkCountry, String mode) {
if (locale == null) {
@@ -1233,6 +1279,22 @@
return imi;
}
+ void putDefaultVoiceInputMethod(String imeId) {
+ if (DEBUG) {
+ Slog.d(TAG, "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId);
+ }
+ putString(Settings.Secure.DEFAULT_VOICE_INPUT_METHOD, imeId);
+ }
+
+ @Nullable
+ String getDefaultVoiceInputMethod() {
+ final String imi = getString(Settings.Secure.DEFAULT_VOICE_INPUT_METHOD, null);
+ if (DEBUG) {
+ Slog.d(TAG, "getDefaultVoiceInputMethodStr: " + imi);
+ }
+ return imi;
+ }
+
boolean isSubtypeSelected() {
return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index f173fc7..4d302b1 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -259,13 +259,16 @@
BroadcastReceiver wifiReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+ if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())
+ || WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED.equals(
+ intent.getAction())) {
sendWifiSettingUpdate(false /* forceUpdate */);
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED);
mContext.registerReceiver(wifiReceiver, filter);
mContext.getContentResolver().registerContentObserver(
@@ -298,7 +301,7 @@
mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
if (userId == getCurrentUserId()) {
- Log.d(TAG, "User: " + userId + " enabled: " + enabled);
+ Log.d(TAG, "User: " + userId + "mic privacy: " + enabled);
sendMicrophoneDisableSettingUpdate(enabled);
}
});
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 3245fdf..7be47a4 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -324,8 +324,11 @@
}
public void onMicrophoneDisableSettingChanged(boolean enabled) {
- sendSettingChanged(android.hardware.contexthub.V1_2.Setting.GLOBAL_MIC_DISABLE,
- enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+ // The SensorPrivacyManager reports if microphone privacy was enabled,
+ // which translates to microphone access being disabled (and vice-versa).
+ // With this in mind, we flip the argument before piping it to CHRE.
+ sendSettingChanged(android.hardware.contexthub.V1_2.Setting.MICROPHONE,
+ enabled ? SettingValue.DISABLED : SettingValue.ENABLED);
}
private void sendSettingChanged(byte setting, byte newValue) {
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index 6201b94..5abc438 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -52,7 +52,8 @@
// Entries added by LockSettingsService once a user's synthetic password is known. At this point
// things are still keyed by userId.
- @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockouts;
+ @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockoutsForFingerprint;
+ @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockoutsForFace;
/**
* Authentication info for a successful user unlock via Synthetic Password. This can be used to
@@ -151,7 +152,8 @@
mContext = context;
mSpManager = spManager;
mHandler = handler;
- mPendingResetLockouts = new ArrayList<>();
+ mPendingResetLockoutsForFingerprint = new ArrayList<>();
+ mPendingResetLockoutsForFace = new ArrayList<>();
}
public void systemReady(@Nullable FingerprintManager fingerprintManager,
@@ -173,17 +175,34 @@
*/
void addPendingLockoutResetForUser(int userId, @NonNull byte[] gatekeeperPassword) {
mHandler.post(() -> {
- Slog.d(TAG, "addPendingLockoutResetForUser: " + userId);
- mPendingResetLockouts.add(new UserAuthInfo(userId, gatekeeperPassword));
+ if (mFaceManager != null && mFaceManager.hasEnrolledTemplates(userId)) {
+ Slog.d(TAG, "Face addPendingLockoutResetForUser: " + userId);
+ mPendingResetLockoutsForFace.add(new UserAuthInfo(userId, gatekeeperPassword));
+ }
+
+ if (mFingerprintManager != null
+ && mFingerprintManager.hasEnrolledFingerprints(userId)) {
+ Slog.d(TAG, "Fingerprint addPendingLockoutResetForUser: " + userId);
+ mPendingResetLockoutsForFingerprint.add(new UserAuthInfo(userId,
+ gatekeeperPassword));
+ }
});
}
void processPendingLockoutResets() {
mHandler.post(() -> {
- Slog.d(TAG, "processPendingLockoutResets: " + mPendingResetLockouts.size());
- processPendingLockoutsForFingerprint(new ArrayList<>(mPendingResetLockouts));
- processPendingLockoutsForFace(new ArrayList<>(mPendingResetLockouts));
- mPendingResetLockouts.clear();
+ if (!mPendingResetLockoutsForFace.isEmpty()) {
+ Slog.d(TAG, "Processing pending resetLockout for face");
+ processPendingLockoutsForFace(new ArrayList<>(mPendingResetLockoutsForFace));
+ mPendingResetLockoutsForFace.clear();
+ }
+
+ if (!mPendingResetLockoutsForFingerprint.isEmpty()) {
+ Slog.d(TAG, "Processing pending resetLockout for fingerprint");
+ processPendingLockoutsForFingerprint(
+ new ArrayList<>(mPendingResetLockoutsForFingerprint));
+ mPendingResetLockoutsForFingerprint.clear();
+ }
});
}
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index cb0a6688..bb996a0 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -64,13 +64,6 @@
private static final int PACKAGE_MANAGER_COMMON_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- /**
- * Denotes the duration during which a media button receiver will be exempted from
- * FGS-from-BG restriction and so will be allowed to start an FGS even if it is in the
- * background state while it receives a media key event.
- */
- private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000;
-
private final int mUserId;
private final PendingIntent mPendingIntent;
private final ComponentName mComponentName;
@@ -185,10 +178,13 @@
* Ignored if there's no valid pending intent.
* @param handler handler to be used to call onFinishedListener
* Ignored if there's no valid pending intent.
+ * @param fgsAllowlistDurationMs duration for which the media button receiver will be
+ * allowed to start FGS from BG.
* @see PendingIntent#send(Context, int, Intent, PendingIntent.OnFinished, Handler)
*/
public boolean send(Context context, KeyEvent keyEvent, String callingPackageName,
- int resultCode, PendingIntent.OnFinished onFinishedListener, Handler handler) {
+ int resultCode, PendingIntent.OnFinished onFinishedListener, Handler handler,
+ long fgsAllowlistDurationMs) {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
@@ -196,7 +192,7 @@
mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackageName);
final BroadcastOptions options = BroadcastOptions.makeBasic();
- options.setTemporaryAppAllowlist(FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS,
+ options.setTemporaryAppAllowlist(fgsAllowlistDurationMs,
PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
PowerWhitelistManager.REASON_MEDIA_BUTTON, "");
if (mPendingIntent != null) {
diff --git a/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java b/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
new file mode 100644
index 0000000..9bb8e2e
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.Context;
+import android.provider.DeviceConfig;
+import android.text.TextUtils;
+
+import java.io.PrintWriter;
+import java.util.Set;
+
+class MediaSessionDeviceConfig {
+ /**
+ * Denotes the duration for which a media button receiver will be exempted from
+ * FGS-from-BG restriction and so will be allowed to start an FGS even if it is in the
+ * background state while it receives a media key event.
+ */
+ private static final String KEY_MEDIA_BUTTON_RECEIVER_FGS_ALLOWLIST_DURATION_MS =
+ "media_button_receiver_fgs_allowlist_duration_ms";
+ private static final long DEFAULT_MEDIA_BUTTON_RECEIVER_FGS_ALLOWLIST_DURATION_MS = 10_000;
+ private static volatile long sMediaButtonReceiverFgsAllowlistDurationMs =
+ DEFAULT_MEDIA_BUTTON_RECEIVER_FGS_ALLOWLIST_DURATION_MS;
+
+ /**
+ * Denotes the duration for which an app receiving a media session callback will be
+ * exempted from FGS-from-BG restriction and so will be allowed to start an FGS even if
+ * it is in the background state while it receives a media session callback.
+ */
+ private static final String KEY_MEDIA_SESSION_CALLBACK_FGS_ALLOWLIST_DURATION_MS =
+ "media_session_calback_fgs_allowlist_duration_ms";
+ private static final long DEFAULT_MEDIA_SESSION_CALLBACK_FGS_ALLOWLIST_DURATION_MS = 10_000;
+ private static volatile long sMediaSessionCallbackFgsAllowlistDurationMs =
+ DEFAULT_MEDIA_SESSION_CALLBACK_FGS_ALLOWLIST_DURATION_MS;
+
+ private static void refresh(DeviceConfig.Properties properties) {
+ final Set<String> keys = properties.getKeyset();
+ properties.getKeyset().forEach(key -> {
+ switch (key) {
+ case KEY_MEDIA_BUTTON_RECEIVER_FGS_ALLOWLIST_DURATION_MS:
+ sMediaButtonReceiverFgsAllowlistDurationMs = properties.getLong(key,
+ DEFAULT_MEDIA_BUTTON_RECEIVER_FGS_ALLOWLIST_DURATION_MS);
+ break;
+ case KEY_MEDIA_SESSION_CALLBACK_FGS_ALLOWLIST_DURATION_MS:
+ sMediaSessionCallbackFgsAllowlistDurationMs = properties.getLong(key,
+ DEFAULT_MEDIA_SESSION_CALLBACK_FGS_ALLOWLIST_DURATION_MS);
+ break;
+ }
+ });
+ }
+
+ public static void initialize(Context context) {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_MEDIA,
+ context.getMainExecutor(), properties -> refresh(properties));
+ refresh(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_MEDIA));
+ }
+
+ /**
+ * Returns the duration for which a media button receiver will be exempted from
+ * FGS-from-BG restriction and so will be allowed to start an FGS even if it is in the
+ * background state while it receives a media key event.
+ */
+ public static long getMediaButtonReceiverFgsAllowlistDurationMs() {
+ return sMediaButtonReceiverFgsAllowlistDurationMs;
+ }
+
+ /**
+ * Returns the duration for which an app receiving a media session callback will be
+ * exempted from FGS-from-BG restriction and so will be allowed to start an FGS even if
+ * it is in the background state while it receives a media session callback.
+ */
+ public static long getMediaSessionCallbackFgsAllowlistDurationMs() {
+ return sMediaSessionCallbackFgsAllowlistDurationMs;
+ }
+
+ public static void dump(PrintWriter pw, String prefix) {
+ pw.println("Media session config:");
+ final String dumpFormat = prefix + " %s: [cur: %s, def: %s]";
+ pw.println(TextUtils.formatSimple(dumpFormat,
+ KEY_MEDIA_BUTTON_RECEIVER_FGS_ALLOWLIST_DURATION_MS,
+ sMediaButtonReceiverFgsAllowlistDurationMs,
+ DEFAULT_MEDIA_BUTTON_RECEIVER_FGS_ALLOWLIST_DURATION_MS));
+ pw.println(TextUtils.formatSimple(dumpFormat,
+ KEY_MEDIA_SESSION_CALLBACK_FGS_ALLOWLIST_DURATION_MS,
+ sMediaSessionCallbackFgsAllowlistDurationMs,
+ DEFAULT_MEDIA_SESSION_CALLBACK_FGS_ALLOWLIST_DURATION_MS));
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a30d993..50cfe1f 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -117,13 +117,6 @@
*/
private static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
- /**
- * Denotes the duration during which an app receiving a media session callback will be
- * exempted from FGS-from-BG restriction and so will be allowed to start an FGS even if it is
- * in the background state while it receives a media session callback.
- */
- private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000;
-
private final Context mContext;
private final SessionManagerImpl mSessionManagerImpl;
private final MessageHandler mHandler = new MessageHandler();
@@ -244,6 +237,9 @@
mCommunicationManager.registerSessionCallback(new HandlerExecutor(mHandler),
mSession2TokenCallback);
break;
+ case PHASE_ACTIVITY_MANAGER_READY:
+ MediaSessionDeviceConfig.initialize(mContext);
+ break;
}
}
@@ -564,7 +560,7 @@
PowerExemptionManager.class);
powerExemptionManager.addToTemporaryAllowList(targetPackage,
PowerExemptionManager.REASON_MEDIA_SESSION_CALLBACK, reason,
- FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS);
+ MediaSessionDeviceConfig.getMediaSessionCallbackFgsAllowlistDurationMs());
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1979,6 +1975,7 @@
}
mAudioPlayerStateMonitor.dump(mContext, pw, "");
}
+ MediaSessionDeviceConfig.dump(pw, "");
}
/**
@@ -2262,7 +2259,8 @@
boolean sent = mediaButtonReceiverHolder.send(
mContext, keyEvent, callingPackageName,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver,
- mHandler);
+ mHandler,
+ MediaSessionDeviceConfig.getMediaButtonReceiverFgsAllowlistDurationMs());
if (sent) {
String pkgName = mediaButtonReceiverHolder.getPackageName();
for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 3859285..40eefca 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -42,6 +42,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_ADMIN_DISABLED;
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
@@ -76,7 +77,6 @@
import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
-import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_MASK;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index de5aae0..19f5e3c 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -24,6 +24,7 @@
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
import static android.net.NetworkStack.checkNetworkStackPermission;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
@@ -65,6 +66,7 @@
import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE;
import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES;
import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -100,7 +102,9 @@
import android.net.NetworkStats;
import android.net.NetworkStats.NonMonotonicObserver;
import android.net.NetworkStatsHistory;
+import android.net.NetworkSpecifier;
import android.net.NetworkTemplate;
+import android.net.TelephonyNetworkSpecifier;
import android.net.TrafficStats;
import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
@@ -1320,8 +1324,9 @@
ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
ident.getRoaming(), true /* metered */,
true /* onDefaultNetwork */, ident.getOemManaged());
- findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
+ final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot);
+ findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, ifaceVt).add(vtIdent);
}
if (isMobile) {
@@ -1377,6 +1382,20 @@
mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]);
}
+ private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) {
+ if (!state.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ throw new IllegalArgumentException("Mobile state need capability TRANSPORT_CELLULAR");
+ }
+
+ final NetworkSpecifier spec = state.networkCapabilities.getNetworkSpecifier();
+ if (spec instanceof TelephonyNetworkSpecifier) {
+ return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
+ } else {
+ Slog.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
+ return INVALID_SUBSCRIPTION_ID;
+ }
+ }
+
/**
* For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through
* {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 3007515..c4a59c2 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -287,6 +287,7 @@
&& !mDefaultComponents.contains(currentComponent)) {
if (approved.remove(currentComponent.flattenToString())) {
disabledComponents.add(currentComponent);
+ clearUserSetFlagLocked(currentComponent, userId);
changed = true;
}
}
@@ -309,6 +310,12 @@
return changes;
}
+ private boolean clearUserSetFlagLocked(ComponentName component, int userId) {
+ String approvedValue = getApprovedValue(component.flattenToString());
+ ArraySet<String> userSet = mUserSetServices.get(userId);
+ return userSet != null && userSet.remove(approvedValue);
+ }
+
protected int getBindFlags() {
return BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 08dbd77..68990ba 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3617,34 +3617,6 @@
}
@Override
- public void deleteConversationNotificationChannels(String pkg, int uid,
- String conversationId) {
- checkCallerIsSystem();
- List<NotificationChannel> channels =
- mPreferencesHelper.getNotificationChannelsByConversationId(
- pkg, uid, conversationId);
- if (!channels.isEmpty()) {
- // Preflight for fg service notifications in these channels: do nothing
- // unless they're all eligible
- final int appUserId = UserHandle.getUserId(uid);
- for (NotificationChannel nc : channels) {
- final String channelId = nc.getId();
- mAmi.stopForegroundServicesForChannel(pkg, appUserId, channelId);
- cancelAllNotificationsInt(MY_UID, MY_PID, pkg, nc.getId(), 0, 0, true,
- appUserId, REASON_CHANNEL_REMOVED, null);
- mPreferencesHelper.deleteNotificationChannel(pkg, uid, channelId);
- mListeners.notifyNotificationChannelChanged(pkg,
- UserHandle.getUserHandleForUid(uid),
- mPreferencesHelper.getNotificationChannel(
- pkg, uid, channelId, true),
- NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- }
- handleSavePolicyFile();
- }
- }
-
-
- @Override
public NotificationChannelGroup getNotificationChannelGroup(String pkg, String groupId) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getNotificationChannelGroupWithChannels(
@@ -10675,8 +10647,6 @@
* TODO(b/161957908): Remove dogfooder toast.
*/
private class NotificationTrampolineCallback implements BackgroundActivityStartCallback {
- private final Set<String> mPackagesShown = new ArraySet<>();
-
@Override
public boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid,
String packageName) {
@@ -10689,16 +10659,12 @@
}
String logcatMessage =
"Indirect notification activity start (trampoline) from " + packageName;
- // Call to toast() method is posted to mHandler below to offload PM lookup from the
- // activity start path
if (CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid)) {
- mHandler.post(() -> toast(packageName, uid, /* blocked */ true));
+ // Post toast() call to mHandler to offload PM lookup from the activity start path
+ mHandler.post(() -> toast(packageName, uid));
Slog.e(TAG, logcatMessage + " blocked");
return false;
} else {
- if (mPackagesShown.add(packageName)) {
- mHandler.post(() -> toast(packageName, uid, /* blocked */ false));
- }
Slog.w(TAG, logcatMessage + ", this should be avoided for performance reasons");
return true;
}
@@ -10713,7 +10679,7 @@
&& !CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid);
}
- private void toast(String packageName, int uid, boolean blocked) {
+ private void toast(String packageName, int uid) {
final CharSequence label;
try {
label = mPackageManagerClient.getApplicationLabel(
@@ -10724,8 +10690,7 @@
return;
}
mUiHandler.post(() -> Toast.makeText(getUiContext(),
- label + " launch " + (blocked ? "blocked" : "will be blocked")
- + "\ng.co/dev/trampolines", Toast.LENGTH_LONG).show());
+ label + " launch blocked\ng.co/dev/trampolines", Toast.LENGTH_LONG).show());
}
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 044e186..27e0ffc 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -18,6 +18,7 @@
import static android.app.ActivityOptions.KEY_SPLASH_SCREEN_THEME;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_MUTABLE;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.pm.LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS;
@@ -648,6 +649,43 @@
}
}
+ /**
+ * Returns the intents for a specific shortcut.
+ */
+ @Nullable
+ @Override
+ public PendingIntent getShortcutIntent(@NonNull final String callingPackage,
+ @NonNull final String packageName, @NonNull final String shortcutId,
+ @Nullable final Bundle opts, @NonNull final UserHandle user)
+ throws RemoteException {
+ Objects.requireNonNull(callingPackage);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(shortcutId);
+ Objects.requireNonNull(user);
+
+ ensureShortcutPermission(callingPackage);
+ if (!canAccessProfile(user.getIdentifier(), "Cannot get shortcuts")) {
+ return null;
+ }
+
+ final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
+ getCallingUserId(), callingPackage, packageName, shortcutId,
+ user.getIdentifier(), injectBinderCallingPid(), injectBinderCallingUid());
+ if (intents == null || intents.length == 0) {
+ return null;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return injectCreatePendingIntent(mContext.createPackageContextAsUser(packageName,
+ 0, user), 0 /* requestCode */, intents, FLAG_MUTABLE, opts, user);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Cannot create pending intent from shortcut " + shortcutId, e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return null;
+ }
+
@Override
public boolean isPackageEnabled(String callingPackage, String packageName, UserHandle user)
throws RemoteException {
@@ -756,6 +794,13 @@
callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
}
+ @VisibleForTesting
+ PendingIntent injectCreatePendingIntent(Context context, int requestCode,
+ @NonNull Intent[] intents, int flags, Bundle options, UserHandle user) {
+ return PendingIntent.getActivitiesAsUser(context, requestCode, intents, flags, options,
+ user);
+ }
+
@Override
public ParceledListSlice getShortcuts(@NonNull final String callingPackage,
@NonNull final ShortcutQueryWrapper query, @NonNull final UserHandle targetUser) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index e0a39f3..b02b8f2 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -258,23 +258,22 @@
packageStats, options.isDowngrade(), profileName, dexMetadataPath,
options.getCompilationReason());
- // Only report metrics for base apk for now.
- // TODO: add ISA and APK type to metrics.
// OTAPreopt doesn't have stats so don't report in that case.
- if (pkg.getBaseApkPath().equals(path) && packageStats != null) {
+ if (packageStats != null) {
Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics");
try {
long sessionId = Math.randomLongInternal();
ArtStatsLogUtils.writeStatsLog(
mArtStatsLogger,
sessionId,
- path,
compilerFilter,
sharedGid,
packageStats.getCompileTime(path),
dexMetadataPath,
options.getCompilationReason(),
- newResult);
+ newResult,
+ ArtStatsLogUtils.getApkType(path),
+ dexCodeIsa);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index a5e28f1..0a484e2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -88,7 +88,6 @@
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
import libcore.io.IoUtils;
@@ -145,7 +144,6 @@
private final PackageManagerService mPm;
private final ApexManager mApexManager;
private final StagingManager mStagingManager;
- private final PermissionManagerServiceInternal mPermissionManager;
private AppOpsManager mAppOps;
@@ -226,7 +224,6 @@
Supplier<PackageParser2> apexParserSupplier) {
mContext = context;
mPm = pm;
- mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class);
mInstallThread = new HandlerThread(TAG);
mInstallThread.start();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 67638bc..2e6c57c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -46,6 +46,7 @@
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -71,6 +72,7 @@
import android.content.pm.IPackageInstallerSession;
import android.content.pm.IPackageInstallerSessionFileSystemConnector;
import android.content.pm.IPackageLoadingProgressCallback;
+import android.content.pm.InstallSourceInfo;
import android.content.pm.InstallationFile;
import android.content.pm.InstallationFileParcel;
import android.content.pm.PackageInfo;
@@ -92,6 +94,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.FileBridge;
import android.os.FileUtils;
@@ -896,6 +899,14 @@
mInstallSource.installerPackageName, mInstallerUid);
}
+ private static final int USER_ACTION_NOT_NEEDED = 0;
+ private static final int USER_ACTION_REQUIRED = 1;
+ private static final int USER_ACTION_PENDING_APK_PARSING = 2;
+
+ @IntDef({USER_ACTION_NOT_NEEDED, USER_ACTION_REQUIRED, USER_ACTION_PENDING_APK_PARSING})
+ @interface
+ UserActionRequirement {}
+
/**
* Checks if the permissions still need to be confirmed.
*
@@ -904,15 +915,22 @@
*
* @return {@code true} iff we need to ask to confirm the permissions?
*/
- private boolean needToAskForPermissions() {
+ @UserActionRequirement
+ private int computeUserActionRequirement() {
final String packageName;
synchronized (mLock) {
if (mPermissionsManuallyAccepted) {
- return false;
+ return USER_ACTION_NOT_NEEDED;
}
packageName = mPackageName;
}
+ final boolean forcePermissionPrompt =
+ (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0
+ || params.requireUserAction == Boolean.TRUE;
+ if (forcePermissionPrompt) {
+ return USER_ACTION_REQUIRED;
+ }
// It is safe to access mInstallerUid and mInstallSource without lock
// because they are immutable after sealing.
final boolean isInstallPermissionGranted =
@@ -924,19 +942,47 @@
final boolean isUpdatePermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+ final boolean isUpdateWithoutUserActionPermissionGranted = (mPm.checkUidPermission(
+ android.Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION, mInstallerUid)
+ == PackageManager.PERMISSION_GRANTED);
final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
+ final boolean isUpdate = targetPackageUid != -1;
+ final InstallSourceInfo installSourceInfo = isUpdate
+ ? mPm.getInstallSourceInfo(packageName)
+ : null;
+ final String installerPackageName = installSourceInfo != null
+ ? installSourceInfo.getInstallingPackageName()
+ : null;
+ final boolean isInstallerOfRecord = isUpdate
+ && Objects.equals(installerPackageName, getInstallerPackageName());
+ final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
final boolean isPermissionGranted = isInstallPermissionGranted
- || (isUpdatePermissionGranted && targetPackageUid != -1)
- || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
+ || (isUpdatePermissionGranted && isUpdate)
+ || (isSelfUpdatePermissionGranted && isSelfUpdate);
final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
- final boolean forcePermissionPrompt =
- (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
// Device owners and affiliated profile owners are allowed to silently install packages, so
// the permission check is waived if the installer is the device owner.
- return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
- || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner());
+ final boolean noUserActionNecessary = isPermissionGranted || isInstallerRoot
+ || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner();
+
+ if (noUserActionNecessary) {
+ return USER_ACTION_NOT_NEEDED;
+ }
+
+ if (mPm.isInstallDisabledForPackage(installerPackageName, mInstallerUid, userId)) {
+ // show the installer to account for device poslicy or unknown sources use cases
+ return USER_ACTION_REQUIRED;
+ }
+
+ if (params.requireUserAction == Boolean.FALSE
+ && isUpdateWithoutUserActionPermissionGranted
+ && (isInstallerOfRecord || isSelfUpdate)) {
+ return USER_ACTION_PENDING_APK_PARSING;
+ }
+
+ return USER_ACTION_REQUIRED;
}
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
@@ -1109,6 +1155,7 @@
getStagedSessionErrorMessage());
info.createdMillis = createdMillis;
info.updatedMillis = updatedMillis;
+ info.requireUserAction = params.requireUserAction;
}
return info;
}
@@ -2194,7 +2241,7 @@
private void verifyNonStaged()
throws PackageManagerException {
final PackageManagerService.VerificationParams verifyingSession =
- makeVerificationParams();
+ prepareForVerification();
if (verifyingSession == null) {
return;
}
@@ -2211,7 +2258,7 @@
final PackageInstallerSession session = childSessions.get(i);
try {
final PackageManagerService.VerificationParams verifyingChildSession =
- session.makeVerificationParams();
+ session.prepareForVerification();
if (verifyingChildSession != null) {
verifyingChildSessions.add(verifyingChildSession);
}
@@ -2298,51 +2345,78 @@
* in case permissions need to be requested before verification can proceed.
*/
@Nullable
- private PackageManagerService.VerificationParams makeVerificationParams()
+ private PackageManagerService.VerificationParams prepareForVerification()
throws PackageManagerException {
assertNotLocked("makeSessionActive");
+ @UserActionRequirement
+ int userActionRequirement = USER_ACTION_NOT_NEEDED;
// TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
- if (!params.isMultiPackage && needToAskForPermissions()) {
- // User needs to confirm installation;
- // give installer an intent they can use to involve
- // user.
- final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
- intent.setPackage(mPm.getPackageInstallerPackageName());
- intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
-
- // Commit was keeping session marked as active until now; release
- // that extra refcount so session appears idle.
- closeInternal(false);
- return null;
+ if (!params.isMultiPackage) {
+ userActionRequirement = computeUserActionRequirement();
+ if (userActionRequirement == USER_ACTION_REQUIRED) {
+ sendPendingUserActionIntent();
+ return null;
+ } // else, we'll wait until we parse to determine if we need to
}
synchronized (mLock) {
+ if (mRelinquished) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session relinquished");
+ }
+ if (mDestroyed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session destroyed");
+ }
+ if (!mSealed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session not sealed");
+ }
+ PackageLite result = parseApkLite();
+ if (result != null) {
+ mPackageLite = result;
+ mInternalProgress = 0.5f;
+ computeProgressLocked(true);
+
+ extractNativeLibraries(
+ mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
+
+ if (userActionRequirement == USER_ACTION_PENDING_APK_PARSING
+ && (result.getTargetSdk() < Build.VERSION_CODES.Q)) {
+ sendPendingUserActionIntent();
+ return null;
+ }
+ }
return makeVerificationParamsLocked();
}
}
- @GuardedBy("mLock")
- private PackageManagerService.VerificationParams makeVerificationParamsLocked()
- throws PackageManagerException {
- if (mRelinquished) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session relinquished");
+ private void sendPendingUserActionIntent() {
+ // User needs to confirm installation;
+ // give installer an intent they can use to involve
+ // user.
+ final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
+ intent.setPackage(mPm.getPackageInstallerPackageName());
+ intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
}
- if (mDestroyed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session destroyed");
- }
- if (!mSealed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session not sealed");
- }
+ sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
+
+ // Commit was keeping session marked as active until now; release
+ // that extra refcount so session appears idle.
+ closeInternal(false);
+ }
+
+ /**
+ * Prepares staged directory with any inherited APKs and returns the parsed package.
+ */
+ @Nullable
+ private PackageLite parseApkLite() throws PackageManagerException {
+
// TODO(b/136257624): Some logic in this if block probably belongs in
// makeInstallParams().
@@ -2351,8 +2425,8 @@
Objects.requireNonNull(mSigningDetails);
Objects.requireNonNull(mResolvedBaseFile);
- // Inherit any packages and native libraries from existing install that
- // haven't been overridden.
+ // If we haven't already parsed, inherit any packages and native libraries from existing
+ // install that haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
try {
final List<File> fromFiles = mResolvedInheritedFiles;
@@ -2404,16 +2478,17 @@
// above block. Therefore, we need to parse the complete package in stage dir here.
// Besides, PackageLite may be null for staged sessions that don't complete pre-reboot
// verification.
- mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
-
- // TODO: surface more granular state from dexopt
- mInternalProgress = 0.5f;
- computeProgressLocked(true);
-
- extractNativeLibraries(mPackageLite, stageDir, params.abiOverride,
- mayInheritNativeLibs());
+ return getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
}
+ return null;
+ }
+ @GuardedBy("mLock")
+ @Nullable
+ /**
+ * Returns a {@link com.android.server.pm.PackageManagerService.VerificationParams}
+ */
+ private PackageManagerService.VerificationParams makeVerificationParamsLocked() {
final IPackageInstallObserver2 localObserver;
if (!hasParentSessionId()) {
// Avoid attaching this observer to child session since they won't use it.
@@ -2713,9 +2788,10 @@
* <p>
* Note that upgrade compatibility is still performed by
* {@link PackageManagerService}.
+ * @return a {@link PackageLite} representation of the validated APK(s).
*/
@GuardedBy("mLock")
- private void validateApkInstallLocked() throws PackageManagerException {
+ private PackageLite validateApkInstallLocked() throws PackageManagerException {
ApkLite baseApk = null;
PackageLite packageLite = null;
mPackageLite = null;
@@ -2737,6 +2813,7 @@
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Missing existing base package");
}
+
// Default to require only if existing base apk has fs-verity.
mVerityFoundForApks = PackageManagerServiceUtils.isApkVerityEnabled()
&& params.mode == SessionParams.MODE_INHERIT_EXISTING
@@ -3023,6 +3100,7 @@
mIncrementalFileStorages.disallowReadLogs();
}
}
+ return packageLite;
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 764fa02..550e9f8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7121,7 +7121,7 @@
// once we have a booted system.
mInstaller.setWarnIfHeld(mLock);
- PackageParser.readConfigUseRoundIcon(mContext.getResources());
+ ParsingPackageUtils.readConfigUseRoundIcon(mContext.getResources());
mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L);
@@ -8481,7 +8481,8 @@
int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
return null;
}
@@ -8492,8 +8493,7 @@
== PERMISSION_GRANTED
|| mContext.checkCallingOrSelfPermission(DELETE_PACKAGES)
== PERMISSION_GRANTED
- || canRequestPackageInstallsInternal(packageName,
- PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId,
+ || canRequestPackageInstallsInternal(packageName, callingUid, userId,
false /* throwIfPermNotDeclared*/)
|| mContext.checkCallingOrSelfPermission(REQUEST_DELETE_PACKAGES)
== PERMISSION_GRANTED
@@ -27514,51 +27514,60 @@
@Override
public boolean canRequestPackageInstalls(String packageName, int userId) {
- return canRequestPackageInstallsInternal(packageName, 0, userId,
+ return canRequestPackageInstallsInternal(packageName, Binder.getCallingUid(), userId,
true /* throwIfPermNotDeclared*/);
}
- private boolean canRequestPackageInstallsInternal(String packageName, int flags, int userId,
- boolean throwIfPermNotDeclared) {
- int callingUid = Binder.getCallingUid();
- int uid = getPackageUid(packageName, 0, userId);
+ private boolean canRequestPackageInstallsInternal(String packageName, int callingUid,
+ int userId, boolean throwIfPermNotDeclared) {
+ int uid = getPackageUidInternal(packageName, 0, userId, callingUid);
if (callingUid != uid && callingUid != Process.ROOT_UID
&& callingUid != Process.SYSTEM_UID) {
throw new SecurityException(
"Caller uid " + callingUid + " does not own package " + packageName);
}
- if (isInstantApp(packageName, userId)) {
+ if (isInstantAppInternal(packageName, userId, callingUid)) {
return false;
}
+ final AndroidPackage pkg;
synchronized (mLock) {
- final AndroidPackage pkg = mPackages.get(packageName);
- if (pkg == null) {
+ pkg = mPackages.get(packageName);
+ }
+ if (pkg == null) {
+ return false;
+ }
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
+ return false;
+ }
+ if (!pkg.getRequestedPermissions().contains(
+ android.Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
+ final String message = "Need to declare "
+ + android.Manifest.permission.REQUEST_INSTALL_PACKAGES
+ + " to call this api";
+ if (throwIfPermNotDeclared) {
+ throw new SecurityException(message);
+ } else {
+ Slog.e(TAG, message);
return false;
}
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
- return false;
- }
- if (!pkg.getRequestedPermissions().contains(
- android.Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
- final String message = "Need to declare "
- + android.Manifest.permission.REQUEST_INSTALL_PACKAGES
- + " to call this api";
- if (throwIfPermNotDeclared) {
- throw new SecurityException(message);
- } else {
- Slog.e(TAG, message);
- return false;
- }
- }
}
+
+ return !isInstallDisabledForPackage(packageName, uid, userId);
+ }
+
+ /**
+ * Returns true if the system or user is explicitly preventing an otherwise valid installer to
+ * complete an install. This includes checks like unknown sources and user restrictions.
+ */
+ public boolean isInstallDisabledForPackage(String packageName, int uid, int userId) {
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId)
- || mUserManager.hasUserRestriction(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userId)) {
- return false;
+ || mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userId)) {
+ return true;
}
if (mExternalSourcesPolicy != null) {
int isTrusted = mExternalSourcesPolicy.getPackageTrustedToInstallApps(packageName, uid);
- return isTrusted == PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED;
+ return isTrusted != PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED;
}
return false;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 0ddb6cd..853b6a7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -561,7 +561,7 @@
}
final ApkLite apkLite = apkLiteResult.getResult();
final PackageLite pkgLite = new PackageLite(null, apkLite.getPath(), apkLite, null,
- null, null, null, null, null);
+ null, null, null, null, null, apkLite.getTargetSdkVersion());
sessionSize += PackageHelper.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride, fd.getFileDescriptor());
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 8c3c423..fd8ec7f 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -2347,7 +2347,7 @@
}
final List<ShortcutInfo> page = new ArrayList<>(results.size());
for (SearchResult result : results) {
- final ShortcutInfo si = new AppSearchShortcutInfo(result.getDocument())
+ final ShortcutInfo si = new AppSearchShortcutInfo(result.getGenericDocument())
.toShortcutInfo(mShortcutUser.getUserId());
page.add(si);
}
@@ -2398,8 +2398,7 @@
@NonNull final Function<AppSearchSession, CompletableFuture<T>> cb) {
final long callingIdentity = Binder.clearCallingIdentity();
final AppSearchManager.SearchContext searchContext =
- new AppSearchManager.SearchContext.Builder()
- .setDatabaseName(getPackageName()).build();
+ new AppSearchManager.SearchContext.Builder(getPackageName()).build();
final AppSearchSession session;
try {
session = ConcurrentUtils.waitForFutureNoInterrupt(
@@ -2408,6 +2407,8 @@
mAppSearchSession = session;
return cb.apply(mAppSearchSession);
} catch (Exception e) {
+ Slog.e(TAG, "Failed to initiate app search for shortcut package "
+ + getPackageName() + " user " + mShortcutUser.getUserId(), e);
return AndroidFuture.completedFuture(null);
} finally {
Binder.restoreCallingIdentity(callingIdentity);
@@ -2427,7 +2428,8 @@
AppSearchShortcutInfo.SCHEMA_TYPE, true, pi);
}
final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
- session.setSchema(schemaBuilder.build(), mShortcutUser.mExecutor, result -> {
+ session.setSchema(
+ schemaBuilder.build(), mShortcutUser.mExecutor, mShortcutUser.mExecutor, result -> {
if (!result.isSuccess()) {
future.completeExceptionally(
new IllegalArgumentException(result.getErrorMessage()));
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 0b21487..d86438b 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -171,7 +171,7 @@
static final int DEFAULT_MAX_UPDATES_PER_INTERVAL = 10;
@VisibleForTesting
- static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = Integer.MAX_VALUE;
+ static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15;
@VisibleForTesting
static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
@@ -270,6 +270,7 @@
final Context mContext;
private final Object mLock = new Object();
+ private final Object mNonPersistentUsersLock = new Object();
private static List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0);
@@ -310,8 +311,10 @@
/**
* User ID -> ShortcutNonPersistentUser
+ *
+ * Note we use a fine-grained lock for {@link #mShortcutNonPersistentUsers} due to b/183618378.
*/
- @GuardedBy("mLock")
+ @GuardedBy("mNonPersistentUsersLock")
private final SparseArray<ShortcutNonPersistentUser> mShortcutNonPersistentUsers =
new SparseArray<>();
@@ -342,7 +345,7 @@
private final IPackageManager mIPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
- private final UserManagerInternal mUserManagerInternal;
+ final UserManagerInternal mUserManagerInternal;
private final UsageStatsManagerInternal mUsageStatsManagerInternal;
private final ActivityManagerInternal mActivityManagerInternal;
private final IUriGrantsManager mUriGrantsManager;
@@ -1308,7 +1311,7 @@
}
/** Return the non-persistent per-user state. */
- @GuardedBy("mLock")
+ @GuardedBy("mNonPersistentUsersLock")
@NonNull
ShortcutNonPersistentUser getNonPersistentUserLocked(@UserIdInt int userId) {
ShortcutNonPersistentUser ret = mShortcutNonPersistentUsers.get(userId);
@@ -2748,7 +2751,7 @@
if (injectHasAccessShortcutsPermission(callingPid, callingUid)) {
return true;
}
- synchronized (mLock) {
+ synchronized (mNonPersistentUsersLock) {
return getNonPersistentUserLocked(userId).hasHostPackage(callingPackage);
}
}
@@ -2831,7 +2834,7 @@
public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName,
int userId) {
- synchronized (mLock) {
+ synchronized (mNonPersistentUsersLock) {
getNonPersistentUserLocked(userId).setShortcutHostPackage(type, packageName);
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 51cb995..5f0aa03 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -333,10 +333,7 @@
if (!shortcutPackage.rescanPackageIfNeeded(isNewApp, forceRescan)) {
if (isNewApp) {
- final ShortcutPackage sp = mPackages.remove(packageName);
- if (sp != null) {
- sp.removeShortcuts();
- }
+ mPackages.remove(packageName);
}
}
}
@@ -722,6 +719,12 @@
future.completeExceptionally(new RuntimeException("app search manager is null"));
return future;
}
+ if (!mService.mUserManagerInternal.isUserUnlockingOrUnlocked(getUserId())) {
+ // In rare cases the user might be stopped immediate after it started, in these cases
+ // any on-going session will need to be abandoned.
+ future.completeExceptionally(new RuntimeException("User " + getUserId() + " is "));
+ return future;
+ }
final long callingIdentity = Binder.clearCallingIdentity();
try {
mAppSearchManager.createSearchSession(searchContext, mExecutor, result -> {
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index c8dc1b1..f99a3c3 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -39,6 +39,7 @@
private static final String TAG = ArtStatsLogUtils.class.getSimpleName();
private static final String PROFILE_DEX_METADATA = "primary.prof";
private static final String VDEX_DEX_METADATA = "primary.vdex";
+ private static final String BASE_APK= "base.apk";
private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY =
@@ -122,16 +123,34 @@
ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK);
}
+ private static final Map<String, Integer> ISA_MAP = new HashMap();
+
+ static {
+ COMPILE_FILTER_MAP.put("arm", ArtStatsLog.
+ ART_DATUM_REPORTED__ISA__ART_ISA_ARM);
+ COMPILE_FILTER_MAP.put("arm64", ArtStatsLog.
+ ART_DATUM_REPORTED__ISA__ART_ISA_ARM64);
+ COMPILE_FILTER_MAP.put("x86", ArtStatsLog.
+ ART_DATUM_REPORTED__ISA__ART_ISA_X86);
+ COMPILE_FILTER_MAP.put("x86_64", ArtStatsLog.
+ ART_DATUM_REPORTED__ISA__ART_ISA_X86_64);
+ COMPILE_FILTER_MAP.put("mips", ArtStatsLog.
+ ART_DATUM_REPORTED__ISA__ART_ISA_MIPS);
+ COMPILE_FILTER_MAP.put("mips64", ArtStatsLog.
+ ART_DATUM_REPORTED__ISA__ART_ISA_MIPS64);
+ }
+
public static void writeStatsLog(
ArtStatsLogger logger,
long sessionId,
- String path,
String compilerFilter,
int uid,
long compileTime,
String dexMetadataPath,
int compilationReason,
- int result) {
+ int result,
+ int apkType,
+ String isa) {
int dexMetadataType = getDexMetadataType(dexMetadataPath);
logger.write(
sessionId,
@@ -140,7 +159,9 @@
compilerFilter,
ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE,
result,
- dexMetadataType);
+ dexMetadataType,
+ apkType,
+ isa);
logger.write(
sessionId,
uid,
@@ -148,7 +169,16 @@
compilerFilter,
ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
compileTime,
- dexMetadataType);
+ dexMetadataType,
+ apkType,
+ isa);
+ }
+
+ public static int getApkType(String path) {
+ if (path.equals(BASE_APK)) {
+ return ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE;
+ }
+ return ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_SPLIT;
}
private static int getDexMetadataType(String dexMetadataPath) {
@@ -207,7 +237,9 @@
String compilerFilter,
int kind,
long value,
- int dexMetadataType) {
+ int dexMetadataType,
+ int apkType,
+ String isa) {
ArtStatsLog.write(
ArtStatsLog.ART_DATUM_REPORTED,
sessionId,
@@ -220,7 +252,10 @@
ArtStatsLog.ART_DATUM_REPORTED__THREAD_TYPE__ART_THREAD_MAIN,
kind,
value,
- dexMetadataType);
+ dexMetadataType,
+ apkType,
+ ISA_MAP.getOrDefault(isa,
+ ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN));
}
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 629f120..21e44ab 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -407,7 +407,7 @@
retProcs.put(proc.getName(),
new ProcessInfo(proc.getName(), new ArraySet<>(proc.getDeniedPermissions()),
proc.getGwpAsanMode(), proc.getMemtagMode(),
- proc.getNativeHeapZeroInit()));
+ proc.getNativeHeapZeroInitialized()));
}
return retProcs;
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index f0d54b4..9bc9d9f 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -278,7 +278,7 @@
try {
return mContext.getPackageManager().getPermissionInfo(permissionName, 0);
} catch (NameNotFoundException e) {
- Slog.e(TAG, "Permission not found: " + permissionName);
+ Slog.w(TAG, "Permission not found: " + permissionName);
return null;
}
}
@@ -737,10 +737,9 @@
grantPermissionsToSystemPackage(pm, packageName, userId,
CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
- SENSORS_PERMISSIONS, STORAGE_PERMISSIONS);
+ SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId,
- ALWAYS_LOCATION_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS,
- ACTIVITY_RECOGNITION_PERMISSIONS);
+ ALWAYS_LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
}
}
if (locationExtraPackageNames != null) {
@@ -1729,6 +1728,14 @@
@Override
public void grantPermission(@NonNull String permission, @NonNull PackageInfo pkg,
@NonNull UserHandle user) {
+ if (PermissionManager.DEBUG_TRACE_GRANTS
+ && PermissionManager.shouldTraceGrant(
+ pkg.packageName, permission, user.getIdentifier())) {
+ Log.i(PermissionManager.LOG_TAG_TRACE_GRANTS,
+ "PregrantPolicy is granting " + pkg.packageName + " "
+ + permission + " for user " + user.getIdentifier(),
+ new RuntimeException());
+ }
PermissionState state = getPermissionState(permission, pkg, user);
state.initGranted();
state.newGranted = true;
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 3244c44..142b36e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -85,11 +85,13 @@
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.metrics.LogMaker;
import android.os.AsyncTask;
+import android.content.AttributionSource;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
@@ -159,6 +161,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -254,6 +257,10 @@
@NonNull
private final PermissionRegistry mRegistry = new PermissionRegistry();
+ @NonNull
+ private final AttributionSourceRegistry mAttributionSourceRegistry =
+ new AttributionSourceRegistry();
+
@GuardedBy("mLock")
@Nullable
private ArraySet<String> mPrivappPermissionsViolations;
@@ -1338,7 +1345,7 @@
boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) {
if (PermissionManager.DEBUG_TRACE_GRANTS
&& PermissionManager.shouldTraceGrant(packageName, permName, userId)) {
- Log.i(TAG, "System is granting " + packageName + " "
+ Log.i(PermissionManager.LOG_TAG_TRACE_GRANTS, "System is granting " + packageName + " "
+ permName + " for user " + userId + " on behalf of uid " + callingUid
+ " " + mPackageManagerInt.getNameForUid(callingUid),
new RuntimeException());
@@ -1798,9 +1805,12 @@
// PermissionPolicyService will handle the app op for runtime permissions later.
grantRuntimePermissionInternal(packageName, permName, false,
Process.SYSTEM_UID, userId, delayingPermCallback);
- // If permission review is enabled the permissions for a legacy apps
- // are represented as constantly granted runtime ones, so don't revoke.
- } else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+ // In certain cases we should leave the state unchanged:
+ // -- If permission review is enabled the permissions for a legacy apps
+ // are represented as constantly granted runtime ones
+ // -- If the permission was split from a non-runtime permission
+ } else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0
+ && !isPermissionSplitFromNonRuntime(permName, targetSdk)) {
// Otherwise, reset the permission.
revokeRuntimePermissionInternal(packageName, permName, false, Process.SYSTEM_UID,
userId, null, delayingPermCallback);
@@ -1833,6 +1843,27 @@
}
/**
+ * Determine if the given permission should be treated as split from a
+ * non-runtime permission for an application targeting the given SDK level.
+ */
+ private boolean isPermissionSplitFromNonRuntime(String permName, int targetSdk) {
+ final List<PermissionManager.SplitPermissionInfo> splitPerms = getSplitPermissionInfos();
+ final int size = splitPerms.size();
+ for (int i = 0; i < size; i++) {
+ final PermissionManager.SplitPermissionInfo splitPerm = splitPerms.get(i);
+ if (targetSdk < splitPerm.getTargetSdk()
+ && splitPerm.getNewPermissions().contains(permName)) {
+ synchronized (mLock) {
+ final Permission perm =
+ mRegistry.getPermission(splitPerm.getSplitPermission());
+ return perm != null && !perm.isRuntime();
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* This change makes it so that apps are told to show rationale for asking for background
* location access every time they request.
*/
@@ -3207,6 +3238,16 @@
}
@Override
+ public @NonNull AttributionSource registerAttributionSource(@NonNull AttributionSource source) {
+ return mAttributionSourceRegistry.registerAttributionSource(source);
+ }
+
+ @Override
+ public boolean isRegisteredAttributionSource(@NonNull AttributionSource source) {
+ return mAttributionSourceRegistry.isRegisteredAttributionSource(source);
+ }
+
+ @Override
public List<String> getAutoRevokeExemptionRequestedPackages(int userId) {
return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISCOURAGED, userId);
}
@@ -5241,4 +5282,73 @@
|| mDelegatedPermissionNames.contains(permissionName);
}
}
+
+ private static final class AttributionSourceRegistry {
+ private final Object mLock = new Object();
+
+ private final WeakHashMap<IBinder, AttributionSource> mAttributions = new WeakHashMap<>();
+
+ public @NonNull AttributionSource registerAttributionSource(
+ @NonNull AttributionSource source) {
+ // Here we keep track of attribution sources that were created by an app
+ // from an attribution chain that called into the app and the apps's
+ // own attribution source. An app can register an attribution chain up
+ // to itself inclusive if and only if it is adding a node for itself which
+ // optionally points to an attribution chain that was created by each
+ // preceding app recursively up to the beginning of the chain.
+ // The only special case is when the first app in the attribution chain
+ // creates a source that points to another app (not a chain of apps). We
+ // allow this even if the source the app points to is not registered since
+ // in app ops we allow every app to blame every other app (untrusted if not
+ // holding a special permission).
+ // This technique ensures that a bad actor in the middle of the attribution
+ // chain can neither prepend nor append an invalid attribution sequence, i.e.
+ // a sequence that is not constructed by trusted sources up to the that bad
+ // actor's app.
+ // Note that passing your attribution source to another app means you allow
+ // it to blame private data access on your app. This can be mediated by the OS
+ // in, which case security is already enforced; by other app's code running in
+ // your process calling into the other app, in which case it can already access
+ // the private data in your process; or by you explicitly calling to another
+ // app passing the source, in which case you must trust the other side;
+
+ final int callingUid = Binder.getCallingUid();
+ if (source.getUid() != callingUid) {
+ throw new SecurityException("Cannot register attribution source for uid:"
+ + source.getUid() + " from uid:" + callingUid);
+ }
+
+ final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ if (packageManagerInternal.getPackageUid(source.getPackageName(), 0,
+ UserHandle.getUserId(callingUid)) != source.getUid()) {
+ throw new SecurityException("Cannot register attribution source for package:"
+ + source.getPackageName() + " from uid:" + callingUid);
+ }
+
+ final AttributionSource next = source.getNext();
+ if (next != null && next.getNext() != null
+ && !isRegisteredAttributionSource(next)) {
+ throw new SecurityException("Cannot register forged attribution source:"
+ + source);
+ }
+
+ synchronized (mLock) {
+ final IBinder token = new Binder();
+ final AttributionSource result = source.withToken(token);
+ mAttributions.put(token, result);
+ return result;
+ }
+ }
+
+ public boolean isRegisteredAttributionSource(@NonNull AttributionSource source) {
+ synchronized (mLock) {
+ final AttributionSource cachedSource = mAttributions.get(source.getToken());
+ if (cachedSource != null) {
+ return cachedSource.equals(source);
+ }
+ return false;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index f4bcd3e..0b48b5c 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -70,11 +70,8 @@
break;
default:
if (!proxy.isCallerVerifier(callingUid)) {
- mContext.enforcePermission(
- android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
- Binder.getCallingPid(), callingUid,
- "Caller " + callingUid
- + " is not allowed to query domain verification state");
+ throw new SecurityException(
+ "Caller is not allowed to query domain verification state");
}
mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 1b2ff08..63515b9 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -24,7 +24,6 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IntentFilterVerificationInfo;
@@ -92,7 +91,6 @@
* commands.
*/
@ChangeId
- @Disabled
private static final long SETTINGS_API_V2 = 178111421;
/**
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index c965390..5db63c6 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -20,13 +20,18 @@
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
+import android.content.AttributionSource;
import android.location.LocationManagerInternal;
+import android.os.IBinder;
import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
import java.util.Set;
@@ -52,7 +57,7 @@
@GuardedBy("mLock - writes only - see above")
@NonNull
private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
- new ConcurrentHashMap();
+ new ConcurrentHashMap<>();
public AppOpsPolicy() {
final LocationManagerInternal locationManagerInternal = LocalServices.getService(
@@ -112,22 +117,59 @@
@Override
public int noteOperation(int code, int uid, @Nullable String packageName,
- @Nullable String featureId, boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer, String, String,
- Boolean, String, Boolean, Integer> superImpl) {
- if (isHandledOp(code)) {
+ @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable
+ String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer,
+ String, String, Boolean, String, Boolean, Integer> superImpl) {
+ return superImpl.apply(resolveOpCode(code, uid, packageName, attributionTag), uid,
+ packageName, attributionTag, shouldCollectAsyncNotedOp,
+ message, shouldCollectMessage);
+ }
+
+ @Override
+ public int noteProxyOperation(int code, @NonNull AttributionSource attributionSource,
+ boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull HexFunction<Integer,
+ AttributionSource, Boolean, String, Boolean, Boolean, Integer> superImpl) {
+ return superImpl.apply(resolveOpCode(code, attributionSource.getUid(),
+ attributionSource.getPackageName(), attributionSource.getAttributionTag()),
+ attributionSource, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ skipProxyOperation);
+ }
+
+ @Override
+ public int startProxyOperation(IBinder token, int code,
+ @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation, @NonNull OctFunction<IBinder, Integer, AttributionSource,
+ Boolean, Boolean, String, Boolean, Boolean, Integer> superImpl) {
+ return superImpl.apply(token, resolveOpCode(code, attributionSource.getUid(),
+ attributionSource.getPackageName(), attributionSource.getAttributionTag()),
+ attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation);
+ }
+
+ @Override
+ public void finishProxyOperation(IBinder clientId, int code,
+ @NonNull AttributionSource attributionSource,
+ @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl) {
+ superImpl.apply(clientId, resolveOpCode(code, attributionSource.getUid(),
+ attributionSource.getPackageName(), attributionSource.getAttributionTag()),
+ attributionSource);
+ }
+
+ private int resolveOpCode(int code, int uid, @NonNull String packageName,
+ @Nullable String attributionTag) {
+ if (isHandledOp(code) && attributionTag != null) {
// Only a single lookup from the underlying concurrent data structure
final ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
if (uidTags != null) {
final ArraySet<String> packageTags = uidTags.get(packageName);
- if (packageTags != null && packageTags.contains(featureId)) {
- return superImpl.apply(resolveLocationOp(code), uid, packageName, featureId,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ if (packageTags != null && packageTags.contains(attributionTag)) {
+ return resolveHandledOp(code);
}
}
}
- return superImpl.apply(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage);
+ return code;
}
private static boolean isHandledOp(int code) {
@@ -139,7 +181,7 @@
return false;
}
- private static int resolveLocationOp(int code) {
+ private static int resolveHandledOp(int code) {
switch (code) {
case AppOpsManager.OP_FINE_LOCATION:
return AppOpsManager.OP_FINE_LOCATION_SOURCE;
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 7f55723..268de3e 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -18,11 +18,13 @@
import static android.view.KeyEvent.KEYCODE_POWER;
import android.os.SystemClock;
+import android.util.Log;
import android.util.SparseLongArray;
import android.view.KeyEvent;
import com.android.internal.util.ToBooleanFunction;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -89,8 +91,8 @@
@Override
public String toString() {
- return "KeyCode1 = " + KeyEvent.keyCodeToString(mKeyCode1)
- + ", KeyCode2 = " + KeyEvent.keyCodeToString(mKeyCode2);
+ return KeyEvent.keyCodeToString(mKeyCode1) + " + "
+ + KeyEvent.keyCodeToString(mKeyCode2);
}
}
@@ -151,6 +153,7 @@
if (!rule.shouldInterceptKeys(mDownTimes)) {
return false;
}
+ Log.v(TAG, "Performing combination rule : " + rule);
rule.execute();
mTriggeredRule = rule;
return true;
@@ -230,4 +233,11 @@
}
return false;
}
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "KeyCombination rules:");
+ forAllRules(mRules, (rule)-> {
+ pw.println(prefix + " " + rule);
+ });
+ }
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 9077433..89d5415 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -578,7 +578,7 @@
private final @NonNull AppOpsManager mAppOpsManager;
private final @NonNull AppOpsManagerInternal mAppOpsManagerInternal;
- private final @NonNull ArrayMap<String, PermissionInfo> mRuntimePermissionInfos;
+ private final @NonNull ArrayMap<String, PermissionInfo> mRuntimeAndTheirBgPermissionInfos;
/**
* All ops that need to be flipped to allow.
@@ -618,7 +618,7 @@
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
- mRuntimePermissionInfos = new ArrayMap<>();
+ mRuntimeAndTheirBgPermissionInfos = new ArrayMap<>();
PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
PermissionManagerServiceInternal.class);
List<PermissionInfo> permissionInfos =
@@ -627,7 +627,30 @@
int permissionInfosSize = permissionInfos.size();
for (int i = 0; i < permissionInfosSize; i++) {
PermissionInfo permissionInfo = permissionInfos.get(i);
- mRuntimePermissionInfos.put(permissionInfo.name, permissionInfo);
+ mRuntimeAndTheirBgPermissionInfos.put(permissionInfo.name, permissionInfo);
+ // Make sure we scoop up all background permissions as they may not be runtime
+ if (permissionInfo.backgroundPermission != null) {
+ String backgroundNonRuntimePermission = permissionInfo.backgroundPermission;
+ for (int j = 0; j < permissionInfosSize; j++) {
+ PermissionInfo bgPermissionCandidate = permissionInfos.get(j);
+ if (permissionInfo.backgroundPermission.equals(
+ bgPermissionCandidate.name)) {
+ backgroundNonRuntimePermission = null;
+ break;
+ }
+ }
+ if (backgroundNonRuntimePermission != null) {
+ try {
+ PermissionInfo backgroundPermissionInfo = mPackageManager
+ .getPermissionInfo(backgroundNonRuntimePermission, 0);
+ mRuntimeAndTheirBgPermissionInfos.put(backgroundPermissionInfo.name,
+ backgroundPermissionInfo);
+ } catch (NameNotFoundException e) {
+ Slog.w(LOG_TAG, "Unknown background permission: "
+ + backgroundNonRuntimePermission);
+ }
+ }
+ }
}
}
@@ -691,7 +714,7 @@
*/
private void addAppOps(@NonNull PackageInfo packageInfo, @NonNull AndroidPackage pkg,
@NonNull String permissionName) {
- PermissionInfo permissionInfo = mRuntimePermissionInfos.get(permissionName);
+ PermissionInfo permissionInfo = mRuntimeAndTheirBgPermissionInfos.get(permissionName);
if (permissionInfo == null) {
return;
}
@@ -726,7 +749,7 @@
boolean shouldGrantAppOp = shouldGrantAppOp(packageInfo, pkg, permissionInfo);
if (shouldGrantAppOp) {
if (permissionInfo.backgroundPermission != null) {
- PermissionInfo backgroundPermissionInfo = mRuntimePermissionInfos.get(
+ PermissionInfo backgroundPermissionInfo = mRuntimeAndTheirBgPermissionInfos.get(
permissionInfo.backgroundPermission);
boolean shouldGrantBackgroundAppOp = backgroundPermissionInfo != null
&& shouldGrantAppOp(packageInfo, pkg, backgroundPermissionInfo);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1b5bb95..f931df8 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -434,7 +434,6 @@
// to hold wakelocks during dispatch and eliminating the critical path.
volatile boolean mPowerKeyHandled;
volatile boolean mBackKeyHandled;
- volatile boolean mBeganFromNonInteractive;
volatile boolean mEndCallKeyHandled;
volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
@@ -876,16 +875,6 @@
if (!mPowerKeyHandled) {
if (!interactive) {
wakeUpFromPowerKey(event.getDownTime());
- if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
- mBeganFromNonInteractive = true;
- } else {
- final int maxCount = getMaxMultiPressPowerCount();
- if (maxCount <= 1) {
- mPowerKeyHandled = true;
- } else {
- mBeganFromNonInteractive = true;
- }
- }
}
} else {
// handled by another power key policy.
@@ -895,7 +884,7 @@
}
}
- private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
+ private void interceptPowerKeyUp(KeyEvent event, boolean canceled) {
final boolean handled = canceled || mPowerKeyHandled;
if (!handled) {
@@ -906,33 +895,36 @@
} else {
// handled by single key or another power key policy.
mSingleKeyGestureDetector.reset();
- finishPowerKeyPress();
}
+
+ finishPowerKeyPress();
}
private void finishPowerKeyPress() {
- mBeganFromNonInteractive = false;
mPowerKeyHandled = false;
if (mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.release();
}
}
- private void powerPress(long eventTime, boolean interactive, int count) {
+ private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
Slog.i(TAG, "Suppressed redundant power key press while "
+ "already in the process of turning the screen on.");
return;
}
+
+ final boolean interactive = Display.isOnState(mDefaultDisplay.getState());
+
Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive
- + " count=" + count + " beganFromNonInteractive=" + mBeganFromNonInteractive +
- " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);
+ + " count=" + count + " beganFromNonInteractive=" + beganFromNonInteractive
+ + " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);
if (count == 2) {
powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
} else if (count == 3) {
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
- } else if (interactive && !mBeganFromNonInteractive) {
+ } else if (interactive && !beganFromNonInteractive) {
switch (mShortPressOnPowerBehavior) {
case SHORT_PRESS_POWER_NOTHING:
break;
@@ -1906,12 +1898,19 @@
@Override
void onPress(long downTime) {
- powerPress(downTime, true, 1 /*count*/);
+ powerPress(downTime, 1 /*count*/,
+ mSingleKeyGestureDetector.beganFromNonInteractive());
finishPowerKeyPress();
}
@Override
void onLongPress(long eventTime) {
+ if (mSingleKeyGestureDetector.beganFromNonInteractive()
+ && !mSupportLongPressPowerWhenNonInteractive) {
+ Slog.v(TAG, "Not support long press power when device is not interactive.");
+ return;
+ }
+
powerLongPress(eventTime);
}
@@ -1923,7 +1922,7 @@
@Override
void onMultiPress(long downTime, int count) {
- powerPress(downTime, true, count);
+ powerPress(downTime, count, mSingleKeyGestureDetector.beganFromNonInteractive());
finishPowerKeyPress();
}
}
@@ -2622,23 +2621,19 @@
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
UserHandle.USER_CURRENT_OR_SELF);
}
- float minFloat = mPowerManager.getBrightnessConstraint(
+ float min = mPowerManager.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
- float maxFloat = mPowerManager.getBrightnessConstraint(
+ float max = mPowerManager.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
- float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction;
- float brightnessFloat = Settings.System.getFloatForUser(
- mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT,
- mContext.getDisplay().getBrightnessDefault(),
- UserHandle.USER_CURRENT_OR_SELF);
- brightnessFloat += stepFloat;
+ float step = (max - min) / BRIGHTNESS_STEPS * direction;
+ int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
+ float brightness = mDisplayManager.getBrightness(screenDisplayId);
+ brightness += step;
// Make sure we don't go beyond the limits.
- brightnessFloat = Math.min(maxFloat, brightnessFloat);
- brightnessFloat = Math.max(minFloat, brightnessFloat);
+ brightness = Math.min(max, brightness);
+ brightness = Math.max(min, brightness);
- Settings.System.putFloatForUser(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessFloat,
- UserHandle.USER_CURRENT_OR_SELF);
+ mDisplayManager.setBrightness(screenDisplayId, brightness);
startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
UserHandle.CURRENT_OR_SELF);
}
@@ -3566,7 +3561,7 @@
if (down) {
interceptPowerKeyDown(event, interactiveAndOn);
} else {
- interceptPowerKeyUp(event, interactiveAndOn, canceled);
+ interceptPowerKeyUp(event, canceled);
}
break;
}
@@ -3756,7 +3751,7 @@
}
}
- mSingleKeyGestureDetector.interceptKey(event);
+ mSingleKeyGestureDetector.interceptKey(event, interactive);
}
// The camera gesture will be detected by GestureLauncherService.
@@ -5234,6 +5229,8 @@
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
mGlobalKeyManager.dump(prefix, pw);
+ mKeyCombinationManager.dump(prefix, pw);
+ mSingleKeyGestureDetector.dump(prefix, pw);
if (mWakeGestureListener != null) {
mWakeGestureListener.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index cae2093..3f4d920 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -25,6 +25,7 @@
import android.view.KeyEvent;
import android.view.ViewConfiguration;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -37,7 +38,7 @@
public final class SingleKeyGestureDetector {
private static final String TAG = "SingleKeyGesture";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = PhoneWindowManager.DEBUG_INPUT;
private static final int MSG_KEY_LONG_PRESS = 0;
private static final int MSG_KEY_VERY_LONG_PRESS = 1;
@@ -47,6 +48,7 @@
private final long mVeryLongPressTimeout;
private volatile int mKeyPressCounter;
+ private boolean mBeganFromNonInteractive = false;
private final ArrayList<SingleKeyRule> mRules = new ArrayList();
private SingleKeyRule mActiveRule = null;
@@ -57,7 +59,6 @@
private final Handler mHandler;
private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout();
-
/** Supported gesture flags */
public static final int KEY_LONGPRESS = 1 << 1;
public static final int KEY_VERYLONGPRESS = 1 << 2;
@@ -143,10 +144,10 @@
@Override
public String toString() {
- return "KeyCode = " + KeyEvent.keyCodeToString(mKeyCode)
- + ", long press : " + supportLongPress()
- + ", very Long press : " + supportVeryLongPress()
- + ", max multi press count : " + getMaxMultiPressCount();
+ return "KeyCode=" + KeyEvent.keyCodeToString(mKeyCode)
+ + ", LongPress=" + supportLongPress()
+ + ", VeryLongPress=" + supportVeryLongPress()
+ + ", MaxMultiPressCount=" + getMaxMultiPressCount();
}
}
@@ -161,8 +162,12 @@
mRules.add(rule);
}
- void interceptKey(KeyEvent event) {
+ void interceptKey(KeyEvent event, boolean interactive) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ // Store the non interactive state when first down.
+ if (mDownKeyCode == KeyEvent.KEYCODE_UNKNOWN || mDownKeyCode != event.getKeyCode()) {
+ mBeganFromNonInteractive = !interactive;
+ }
interceptKeyDown(event);
} else {
interceptKeyUp(event);
@@ -268,6 +273,7 @@
Log.i(TAG, "press key " + KeyEvent.keyCodeToString(event.getKeyCode()));
}
mActiveRule.onPress(downTime);
+ reset();
return true;
}
@@ -316,6 +322,17 @@
return false;
}
+ boolean beganFromNonInteractive() {
+ return mBeganFromNonInteractive;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "SingleKey rules:");
+ for (SingleKeyRule rule : mRules) {
+ pw.println(prefix + " " + rule);
+ }
+ }
+
private class KeyHandler extends Handler {
KeyHandler() {
super(Looper.getMainLooper());
@@ -354,7 +371,7 @@
} else {
mActiveRule.onMultiPress(eventTime, mKeyPressCounter);
}
- mKeyPressCounter = 0;
+ reset();
break;
}
}
diff --git a/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java
index 2fcd178..ea93324 100644
--- a/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java
+++ b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java
@@ -125,6 +125,10 @@
return mDisplayGroupIds;
}
+ int getDisplayGroupCountLocked() {
+ return mDisplayGroupIds.length;
+ }
+
int getWakefulnessLocked(int groupId) {
return mDisplayGroupInfos.get(groupId).wakefulness;
}
@@ -248,6 +252,38 @@
return false;
}
+ long getLastUserActivityTimeLocked(int groupId) {
+ return mDisplayGroupInfos.get(groupId).lastUserActivityTime;
+ }
+
+ long getLastUserActivityTimeNoChangeLightsLocked(int groupId) {
+ return mDisplayGroupInfos.get(groupId).lastUserActivityTimeNoChangeLights;
+ }
+
+ int getUserActivitySummaryLocked(int groupId) {
+ return mDisplayGroupInfos.get(groupId).userActivitySummary;
+ }
+
+ void setLastUserActivityTimeLocked(int groupId, long time) {
+ mDisplayGroupInfos.get(groupId).lastUserActivityTime = time;
+ }
+
+ void setLastUserActivityTimeNoChangeLightsLocked(int groupId, long time) {
+ mDisplayGroupInfos.get(groupId).lastUserActivityTimeNoChangeLights = time;
+ }
+
+ void setUserActivitySummaryLocked(int groupId, int summary) {
+ mDisplayGroupInfos.get(groupId).userActivitySummary = summary;
+ }
+
+ int getWakeLockSummaryLocked(int groupId) {
+ return mDisplayGroupInfos.get(groupId).wakeLockSummary;
+ }
+
+ void setWakeLockSummaryLocked(int groupId, int summary) {
+ mDisplayGroupInfos.get(groupId).wakeLockSummary = summary;
+ }
+
/**
* Interface through which an interested party may be informed of {@link DisplayGroup} events.
*/
@@ -265,6 +301,10 @@
public boolean ready;
public long lastPowerOnTime;
public boolean sandmanSummoned;
+ public long lastUserActivityTime;
+ public long lastUserActivityTimeNoChangeLights;
+ public int userActivitySummary;
+ public int wakeLockSummary;
/** {@code true} if this DisplayGroup supports dreaming; otherwise {@code false}. */
public boolean supportsSandman;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index d2a4cd6..8052522 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -29,6 +29,7 @@
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
+import static android.os.PowerManagerInternal.wakefulnessToString;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -92,6 +93,7 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy;
@@ -180,8 +182,8 @@
private static final int DIRTY_VR_MODE_CHANGED = 1 << 13;
// Dirty bit: attentive timer may have timed out
private static final int DIRTY_ATTENTIVE = 1 << 14;
- // Dirty bit: display group power state has changed
- private static final int DIRTY_DISPLAY_GROUP_POWER_UPDATED = 1 << 16;
+ // Dirty bit: display group wakefulness has changed
+ private static final int DIRTY_DISPLAY_GROUP_WAKEFULNESS = 1 << 16;
// Summarizes the state of all active wakelocks.
private static final int WAKE_LOCK_CPU = 1 << 0;
@@ -338,10 +340,6 @@
private @WakeReason int mLastWakeReason;
private int mLastSleepReason;
- // Timestamp of the last call to user activity.
- private long mLastUserActivityTime;
- private long mLastUserActivityTimeNoChangeLights;
-
// Timestamp of last time power boost interaction was sent.
private long mLastInteractivePowerHintTime;
@@ -349,9 +347,6 @@
private long mLastScreenBrightnessBoostTime;
private boolean mScreenBrightnessBoostInProgress;
- // A bitfield that summarizes the effect of the user activity timer.
- private int mUserActivitySummary;
-
// Manages the desired power state of displays. The actual state may lag behind the
// requested because it is updated asynchronously by the display power controller.
private DisplayGroupPowerStateMapper mDisplayGroupPowerStateMapper;
@@ -634,6 +629,13 @@
public void onDisplayGroupEventLocked(int event, int groupId) {
final int oldWakefulness = getWakefulnessLocked();
final int newWakefulness = mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked();
+
+ if (event == DISPLAY_GROUP_ADDED && newWakefulness == WAKEFULNESS_AWAKE) {
+ // Kick user activity to prevent newly added group from timing out instantly.
+ userActivityNoUpdateLocked(groupId, mClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER, /* flags= */ 0, Process.SYSTEM_UID);
+ }
+
if (oldWakefulness != newWakefulness) {
final int reason;
switch (newWakefulness) {
@@ -656,7 +658,7 @@
mContext.getOpPackageName(), "groupId: " + groupId);
}
- mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
+ mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
updatePowerStateLocked();
}
}
@@ -1059,6 +1061,10 @@
private void onFlip(boolean isFaceDown) {
long millisUntilNormalTimeout = 0;
synchronized (mLock) {
+ if (!mBootCompleted) {
+ return;
+ }
+
mIsFaceDown = isFaceDown;
if (isFaceDown) {
final long currentTime = mClock.uptimeMillis();
@@ -1066,8 +1072,9 @@
final long sleepTimeout = getSleepTimeoutLocked(-1L);
final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, -1L);
millisUntilNormalTimeout =
- mLastUserActivityTime + screenOffTimeout - mClock.uptimeMillis();
- userActivityInternal(mClock.uptimeMillis(),
+ mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
+ Display.DEFAULT_DISPLAY_GROUP) + screenOffTimeout - currentTime;
+ userActivityInternal(Display.DEFAULT_DISPLAY, currentTime,
PowerManager.USER_ACTIVITY_EVENT_FACE_DOWN, /* flags= */0,
Process.SYSTEM_UID);
}
@@ -1341,9 +1348,20 @@
updatePowerStateLocked();
}
- private void acquireWakeLockInternal(IBinder lock, int flags, String tag, String packageName,
- WorkSource ws, String historyTag, int uid, int pid) {
+ private void acquireWakeLockInternal(IBinder lock, int displayId, int flags, String tag,
+ String packageName, WorkSource ws, String historyTag, int uid, int pid) {
synchronized (mLock) {
+ if (displayId != Display.INVALID_DISPLAY) {
+ final DisplayInfo displayInfo =
+ mSystemReady ? mDisplayManagerInternal.getDisplayInfo(displayId) : null;
+ if (displayInfo == null) {
+ Slog.wtf(TAG, "Tried to acquire wake lock for invalid display: " + displayId);
+ return;
+ } else if (!displayInfo.hasAccess(uid)) {
+ throw new SecurityException("Caller does not have access to display");
+ }
+ }
+
if (DEBUG_SPEW) {
Slog.d(TAG, "acquireWakeLockInternal: lock=" + Objects.hashCode(lock)
+ ", flags=0x" + Integer.toHexString(flags)
@@ -1370,8 +1388,8 @@
mUidState.put(uid, state);
}
state.mNumWakeLocks++;
- wakeLock = new WakeLock(lock, flags, tag, packageName, ws, historyTag, uid, pid,
- state);
+ wakeLock = new WakeLock(lock, displayId, flags, tag, packageName, ws, historyTag,
+ uid, pid, state);
try {
lock.linkToDeath(wakeLock, 0);
} catch (RemoteException ex) {
@@ -1645,12 +1663,28 @@
// Called from native code.
private void userActivityFromNative(long eventTime, int event, int displayId, int flags) {
- userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID);
+ userActivityInternal(displayId, eventTime, event, flags, Process.SYSTEM_UID);
}
- private void userActivityInternal(long eventTime, int event, int flags, int uid) {
+ private void userActivityInternal(int displayId, long eventTime, int event, int flags,
+ int uid) {
synchronized (mLock) {
- if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
+ updatePowerStateLocked();
+ }
+ return;
+ }
+
+ final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);
+ if (displayInfo == null) {
+ return;
+ }
+ final int groupId = displayInfo.displayGroupId;
+ if (groupId == Display.INVALID_DISPLAY_GROUP) {
+ return;
+ }
+ if (userActivityNoUpdateLocked(groupId, eventTime, event, flags, uid)) {
updatePowerStateLocked();
}
}
@@ -1658,7 +1692,7 @@
private void onUserAttention() {
synchronized (mLock) {
- if (userActivityNoUpdateLocked(mClock.uptimeMillis(),
+ if (userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_ATTENTION, 0 /* flags */,
Process.SYSTEM_UID)) {
updatePowerStateLocked();
@@ -1667,10 +1701,22 @@
}
private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
+ boolean updatePowerState = false;
+ for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ if (userActivityNoUpdateLocked(id, eventTime, event, flags, uid)) {
+ updatePowerState = true;
+ }
+ }
+
+ return updatePowerState;
+ }
+
+ private boolean userActivityNoUpdateLocked(int groupId, long eventTime, int event, int flags,
+ int uid) {
if (DEBUG_SPEW) {
- Slog.d(TAG, "userActivityNoUpdateLocked: eventTime=" + eventTime
- + ", event=" + event + ", flags=0x" + Integer.toHexString(flags)
- + ", uid=" + uid);
+ Slog.d(TAG, "userActivityNoUpdateLocked: groupId=" + groupId
+ + ", eventTime=" + eventTime + ", event=" + event
+ + ", flags=0x" + Integer.toHexString(flags) + ", uid=" + uid);
}
if (eventTime < mLastSleepTime || eventTime < mLastWakeTime || !mSystemReady) {
@@ -1692,8 +1738,9 @@
mOverriddenTimeout = -1;
}
- if (getWakefulnessLocked() == WAKEFULNESS_ASLEEP
- || getWakefulnessLocked() == WAKEFULNESS_DOZING
+ final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
+ if (wakefulness == WAKEFULNESS_ASLEEP
+ || wakefulness == WAKEFULNESS_DOZING
|| (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {
return false;
}
@@ -1701,9 +1748,13 @@
maybeUpdateForegroundProfileLastActivityLocked(eventTime);
if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
- if (eventTime > mLastUserActivityTimeNoChangeLights
- && eventTime > mLastUserActivityTime) {
- mLastUserActivityTimeNoChangeLights = eventTime;
+ if (eventTime
+ > mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
+ groupId)
+ && eventTime > mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
+ groupId)) {
+ mDisplayGroupPowerStateMapper.setLastUserActivityTimeNoChangeLightsLocked(
+ groupId, eventTime);
mDirty |= DIRTY_USER_ACTIVITY;
if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
mDirty |= DIRTY_QUIESCENT;
@@ -1712,8 +1763,9 @@
return true;
}
} else {
- if (eventTime > mLastUserActivityTime) {
- mLastUserActivityTime = eventTime;
+ if (eventTime > mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
+ groupId)) {
+ mDisplayGroupPowerStateMapper.setLastUserActivityTimeLocked(groupId, eventTime);
mDirty |= DIRTY_USER_ACTIVITY;
if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
mDirty |= DIRTY_QUIESCENT;
@@ -1778,7 +1830,6 @@
setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
opPackageName, details);
mDisplayGroupPowerStateMapper.setLastPowerOnTimeLocked(groupId, eventTime);
- mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -1829,7 +1880,6 @@
if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid);
}
- mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -1862,7 +1912,6 @@
mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true);
setWakefulnessLocked(groupId, WAKEFULNESS_DREAMING, eventTime, uid, /* reason= */
0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
- mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
@@ -1890,7 +1939,6 @@
setWakefulnessLocked(groupId, WAKEFULNESS_ASLEEP, eventTime, uid,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, /* opUid= */ 0,
/* opPackageName= */ null, /* details= */ null);
- mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -1901,8 +1949,14 @@
void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason,
int opUid, String opPackageName, String details) {
if (mDisplayGroupPowerStateMapper.setWakefulnessLocked(groupId, wakefulness)) {
+ mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
setGlobalWakefulnessLocked(mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(),
eventTime, reason, uid, opUid, opPackageName, details);
+ if (wakefulness == WAKEFULNESS_AWAKE) {
+ // Kick user activity to prevent newly awake group from timing out instantly.
+ userActivityNoUpdateLocked(
+ groupId, eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
+ }
}
}
@@ -1972,8 +2026,6 @@
switch (wakefulness) {
case WAKEFULNESS_AWAKE:
mNotifier.onWakeUp(reason, details, uid, opPackageName, opUid);
- userActivityNoUpdateLocked(
- eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
if (sQuiescent) {
mDirty |= DIRTY_QUIESCENT;
}
@@ -2006,6 +2058,11 @@
return mWakefulnessRaw;
}
+ @VisibleForTesting
+ int getWakefulnessLocked(int groupId) {
+ return mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
+ }
+
/**
* Logs the time the device would have spent awake before user activity timeout,
* had the system not been told the user was inactive.
@@ -2163,8 +2220,9 @@
"android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
}
- userActivityNoUpdateLocked(
- now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+
+ userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, now,
+ PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
// only play charging sounds if boot is completed so charging sounds don't play
// with potential notification sounds
@@ -2251,7 +2309,9 @@
*/
@SuppressWarnings("deprecation")
private void updateWakeLockSummaryLocked(int dirty) {
- if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {
+ if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS | DIRTY_DISPLAY_GROUP_WAKEFULNESS))
+ != 0) {
+
mWakeLockSummary = 0;
final int numProfiles = mProfilePowerState.size();
@@ -2259,11 +2319,28 @@
mProfilePowerState.valueAt(i).mWakeLockSummary = 0;
}
+ for (int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ mDisplayGroupPowerStateMapper.setWakeLockSummaryLocked(groupId, 0);
+ }
+
+ int invalidGroupWakeLockSummary = 0;
final int numWakeLocks = mWakeLocks.size();
for (int i = 0; i < numWakeLocks; i++) {
final WakeLock wakeLock = mWakeLocks.get(i);
final int wakeLockFlags = getWakeLockSummaryFlags(wakeLock);
mWakeLockSummary |= wakeLockFlags;
+
+ final int groupId = wakeLock.getDisplayGroupId();
+ if (groupId != Display.INVALID_DISPLAY_GROUP) {
+ int wakeLockSummary = mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(
+ groupId);
+ wakeLockSummary |= wakeLockFlags;
+ mDisplayGroupPowerStateMapper.setWakeLockSummaryLocked(groupId,
+ wakeLockSummary);
+ } else {
+ invalidGroupWakeLockSummary |= wakeLockFlags;
+ }
+
for (int j = 0; j < numProfiles; j++) {
final ProfilePowerState profile = mProfilePowerState.valueAt(j);
if (wakeLockAffectsUser(wakeLock, profile.mUserId)) {
@@ -2272,10 +2349,21 @@
}
}
- mWakeLockSummary = adjustWakeLockSummaryLocked(mWakeLockSummary);
+ for (int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ final int wakeLockSummary = adjustWakeLockSummaryLocked(
+ mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId),
+ invalidGroupWakeLockSummary
+ | mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId));
+ mDisplayGroupPowerStateMapper.setWakeLockSummaryLocked(groupId, wakeLockSummary);
+ }
+
+ mWakeLockSummary = adjustWakeLockSummaryLocked(getWakefulnessLocked(),
+ mWakeLockSummary);
+
for (int i = 0; i < numProfiles; i++) {
final ProfilePowerState profile = mProfilePowerState.valueAt(i);
- profile.mWakeLockSummary = adjustWakeLockSummaryLocked(profile.mWakeLockSummary);
+ profile.mWakeLockSummary = adjustWakeLockSummaryLocked(getWakefulnessLocked(),
+ profile.mWakeLockSummary);
}
if (DEBUG_SPEW) {
@@ -2286,25 +2374,25 @@
}
}
- private int adjustWakeLockSummaryLocked(int wakeLockSummary) {
+ private static int adjustWakeLockSummaryLocked(int wakefulness, int wakeLockSummary) {
// Cancel wake locks that make no sense based on the current state.
- if (getWakefulnessLocked() != WAKEFULNESS_DOZING) {
+ if (wakefulness != WAKEFULNESS_DOZING) {
wakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
}
- if (getWakefulnessLocked() == WAKEFULNESS_ASLEEP
+ if (wakefulness == WAKEFULNESS_ASLEEP
|| (wakeLockSummary & WAKE_LOCK_DOZE) != 0) {
wakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
| WAKE_LOCK_BUTTON_BRIGHT);
- if (getWakefulnessLocked() == WAKEFULNESS_ASLEEP) {
+ if (wakefulness == WAKEFULNESS_ASLEEP) {
wakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF;
}
}
// Infer implied wake locks where necessary based on the current state.
if ((wakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0) {
- if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
+ if (wakefulness == WAKEFULNESS_AWAKE) {
wakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE;
- } else if (getWakefulnessLocked() == WAKEFULNESS_DREAMING) {
+ } else if (wakefulness == WAKEFULNESS_DREAMING) {
wakeLockSummary |= WAKE_LOCK_CPU;
}
}
@@ -2407,106 +2495,124 @@
*/
private void updateUserActivitySummaryLocked(long now, int dirty) {
// Update the status of the user activity timeout timer.
- if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY
- | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
- mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
+ if ((dirty & (DIRTY_DISPLAY_GROUP_WAKEFULNESS | DIRTY_WAKE_LOCKS
+ | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) == 0) {
+ return;
+ }
+ mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
- long nextTimeout = 0;
- if (getWakefulnessLocked() == WAKEFULNESS_AWAKE
- || getWakefulnessLocked() == WAKEFULNESS_DREAMING
- || getWakefulnessLocked() == WAKEFULNESS_DOZING) {
- final long attentiveTimeout = getAttentiveTimeoutLocked();
- final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
- long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout,
- attentiveTimeout);
- final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
- screenOffTimeout =
- getScreenOffTimeoutWithFaceDownLocked(screenOffTimeout, screenDimDuration);
- final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
- final long nextProfileTimeout = getNextProfileTimeoutLocked(now);
-
- mUserActivitySummary = 0;
- if (mLastUserActivityTime >= mLastWakeTime) {
- nextTimeout = mLastUserActivityTime
- + screenOffTimeout - screenDimDuration;
- if (now < nextTimeout) {
- mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
+ final long attentiveTimeout = getAttentiveTimeoutLocked();
+ final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout);
+ long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout,
+ attentiveTimeout);
+ final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
+ screenOffTimeout =
+ getScreenOffTimeoutWithFaceDownLocked(screenOffTimeout, screenDimDuration);
+ final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
+ long nextTimeout = -1;
+ boolean hasUserActivitySummary = false;
+ for (int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ int groupUserActivitySummary = 0;
+ long groupNextTimeout = 0;
+ if (mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != WAKEFULNESS_ASLEEP) {
+ final long lastUserActivityTime =
+ mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(groupId);
+ final long lastUserActivityTimeNoChangeLights =
+ mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
+ groupId);
+ if (lastUserActivityTime >= mLastWakeTime) {
+ groupNextTimeout = lastUserActivityTime + screenOffTimeout - screenDimDuration;
+ if (now < groupNextTimeout) {
+ groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
} else {
- nextTimeout = mLastUserActivityTime + screenOffTimeout;
- if (now < nextTimeout) {
- mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
+ groupNextTimeout = lastUserActivityTime + screenOffTimeout;
+ if (now < groupNextTimeout) {
+ groupUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
}
}
}
- if (mUserActivitySummary == 0
- && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
- nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
- if (now < nextTimeout) {
+ if (groupUserActivitySummary == 0
+ && lastUserActivityTimeNoChangeLights >= mLastWakeTime) {
+ groupNextTimeout = lastUserActivityTimeNoChangeLights + screenOffTimeout;
+ if (now < groupNextTimeout) {
final DisplayPowerRequest displayPowerRequest =
- mDisplayGroupPowerStateMapper.getPowerRequestLocked(
- Display.DEFAULT_DISPLAY_GROUP);
+ mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId);
if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
|| displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
- mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
+ groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
} else if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
- mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
+ groupUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
}
}
}
- if (mUserActivitySummary == 0) {
+ if (groupUserActivitySummary == 0) {
if (sleepTimeout >= 0) {
- final long anyUserActivity = Math.max(mLastUserActivityTime,
- mLastUserActivityTimeNoChangeLights);
+ final long anyUserActivity = Math.max(lastUserActivityTime,
+ lastUserActivityTimeNoChangeLights);
if (anyUserActivity >= mLastWakeTime) {
- nextTimeout = anyUserActivity + sleepTimeout;
- if (now < nextTimeout) {
- mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
+ groupNextTimeout = anyUserActivity + sleepTimeout;
+ if (now < groupNextTimeout) {
+ groupUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
}
}
} else {
- mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
- nextTimeout = -1;
+ groupUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
+ groupNextTimeout = -1;
}
}
- if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
- if ((mUserActivitySummary &
+ if (groupUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM
+ && userInactiveOverride) {
+ if ((groupUserActivitySummary &
(USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
// Device is being kept awake by recent user activity
- if (nextTimeout >= now && mOverriddenTimeout == -1) {
+ if (mOverriddenTimeout == -1) {
// Save when the next timeout would have occurred
- mOverriddenTimeout = nextTimeout;
+ mOverriddenTimeout = groupNextTimeout;
}
}
- mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
- nextTimeout = -1;
+ groupUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
+ groupNextTimeout = -1;
}
- if ((mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
- && (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) == 0) {
- nextTimeout = mAttentionDetector.updateUserActivity(nextTimeout,
+ if ((groupUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+ && (mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId)
+ & WAKE_LOCK_STAY_AWAKE) == 0) {
+ groupNextTimeout = mAttentionDetector.updateUserActivity(groupNextTimeout,
screenDimDuration);
}
- if (nextProfileTimeout > 0) {
- nextTimeout = Math.min(nextTimeout, nextProfileTimeout);
- }
+ hasUserActivitySummary |= groupUserActivitySummary != 0;
- if (mUserActivitySummary != 0 && nextTimeout >= 0) {
- scheduleUserInactivityTimeout(nextTimeout);
+ if (nextTimeout == -1) {
+ nextTimeout = groupNextTimeout;
+ } else if (groupNextTimeout != -1) {
+ nextTimeout = Math.min(nextTimeout, groupNextTimeout);
}
- } else {
- mUserActivitySummary = 0;
}
+ mDisplayGroupPowerStateMapper.setUserActivitySummaryLocked(groupId,
+ groupUserActivitySummary);
+
if (DEBUG_SPEW) {
- Slog.d(TAG, "updateUserActivitySummaryLocked: mWakefulness="
- + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked())
- + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
- + ", nextTimeout=" + TimeUtils.formatUptime(nextTimeout));
+ Slog.d(TAG, "updateUserActivitySummaryLocked: groupId=" + groupId
+ + ", mWakefulness=" + wakefulnessToString(
+ mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId))
+ + ", mUserActivitySummary=0x" + Integer.toHexString(
+ groupUserActivitySummary)
+ + ", nextTimeout=" + TimeUtils.formatUptime(groupNextTimeout));
}
}
+
+ final long nextProfileTimeout = getNextProfileTimeoutLocked(now);
+ if (nextProfileTimeout > 0) {
+ nextTimeout = Math.min(nextTimeout, nextProfileTimeout);
+ }
+
+ if (hasUserActivitySummary && nextTimeout >= 0) {
+ scheduleUserInactivityTimeout(nextTimeout);
+ }
}
private void scheduleUserInactivityTimeout(long timeMs) {
@@ -2539,15 +2645,20 @@
private void updateAttentiveStateLocked(long now, int dirty) {
long attentiveTimeout = getAttentiveTimeoutLocked();
- long goToSleepTime = mLastUserActivityTime + attentiveTimeout;
+ if (attentiveTimeout < 0) {
+ return;
+ }
+ // Attentive state only applies to the default display group.
+ long goToSleepTime = mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
+ Display.DEFAULT_DISPLAY_GROUP) + attentiveTimeout;
long showWarningTime = goToSleepTime - mAttentiveWarningDurationConfig;
boolean warningDismissed = maybeHideInattentiveSleepWarningLocked(now, showWarningTime);
- if (attentiveTimeout >= 0 && (warningDismissed
- || (dirty & (DIRTY_ATTENTIVE | DIRTY_STAY_ON | DIRTY_SCREEN_BRIGHTNESS_BOOST
- | DIRTY_PROXIMITY_POSITIVE | DIRTY_WAKEFULNESS | DIRTY_BOOT_COMPLETED
- | DIRTY_SETTINGS)) != 0)) {
+ if (warningDismissed ||
+ (dirty & (DIRTY_ATTENTIVE | DIRTY_STAY_ON | DIRTY_SCREEN_BRIGHTNESS_BOOST
+ | DIRTY_PROXIMITY_POSITIVE | DIRTY_WAKEFULNESS | DIRTY_BOOT_COMPLETED
+ | DIRTY_SETTINGS)) != 0) {
if (DEBUG_SPEW) {
Slog.d(TAG, "Updating attentive state");
}
@@ -2601,9 +2712,12 @@
return false;
}
- private boolean isAttentiveTimeoutExpired(long now) {
+ private boolean isAttentiveTimeoutExpired(int groupId, long now) {
long attentiveTimeout = getAttentiveTimeoutLocked();
- return attentiveTimeout >= 0 && now >= mLastUserActivityTime + attentiveTimeout;
+ // Attentive state only applies to the default display group.
+ return groupId == Display.DEFAULT_DISPLAY_GROUP && attentiveTimeout >= 0
+ && now >= mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(groupId)
+ + attentiveTimeout;
}
/**
@@ -2703,26 +2817,20 @@
| DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
| DIRTY_DOCK_STATE | DIRTY_ATTENTIVE | DIRTY_SETTINGS
| DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) {
- if (getWakefulnessLocked() == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
- if (DEBUG_SPEW) {
- Slog.d(TAG, "updateWakefulnessLocked: Bed time...");
- }
- final long time = mClock.uptimeMillis();
- if (isAttentiveTimeoutExpired(time)) {
- // TODO (b/175764389): Support per-display timeouts.
- for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ final long time = mClock.uptimeMillis();
+ for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ if (mDisplayGroupPowerStateMapper.getWakefulnessLocked(id) == WAKEFULNESS_AWAKE
+ && isItBedTimeYetLocked(id)) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateWakefulnessLocked: Bed time for group " + id);
+ }
+ if (isAttentiveTimeoutExpired(id, time)) {
changed = sleepDisplayGroupNoUpdateLocked(id, time,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
- }
- } else if (shouldNapAtBedTimeLocked()) {
- // TODO (b/175764389): Support per-display timeouts.
- for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ } else if (shouldNapAtBedTimeLocked()) {
changed = dreamDisplayGroupNoUpdateLocked(id, time, Process.SYSTEM_UID);
- }
- } else {
- // TODO (b/175764389): Support per-display timeouts.
- for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ } else {
changed = sleepDisplayGroupNoUpdateLocked(id, time,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
}
@@ -2743,51 +2851,51 @@
}
/**
- * Returns true if the device should go to sleep now.
- * Also used when exiting a dream to determine whether we should go back
- * to being fully awake or else go to sleep for good.
+ * Returns true if the DisplayGroup with the provided {@code groupId} should go to sleep now.
+ * Also used when exiting a dream to determine whether we should go back to being fully awake or
+ * else go to sleep for good.
*/
- private boolean isItBedTimeYetLocked() {
+ private boolean isItBedTimeYetLocked(int groupId) {
if (!mBootCompleted) {
return false;
}
long now = mClock.uptimeMillis();
- if (isAttentiveTimeoutExpired(now)) {
- return !isBeingKeptFromInattentiveSleepLocked();
+ if (isAttentiveTimeoutExpired(groupId, now)) {
+ return !isBeingKeptFromInattentiveSleepLocked(groupId);
} else {
- return !isBeingKeptAwakeLocked();
+ return !isBeingKeptAwakeLocked(groupId);
}
}
/**
- * Returns true if the device is being kept awake by a wake lock, user activity
- * or the stay on while powered setting. We also keep the phone awake when
- * the proximity sensor returns a positive result so that the device does not
- * lock while in a phone call. This function only controls whether the device
- * will go to sleep or dream which is independent of whether it will be allowed
- * to suspend.
+ * Returns true if the DisplayGroup with the provided {@code groupId} is being kept awake by a
+ * wake lock, user activity or the stay on while powered setting. We also keep the phone awake
+ * when the proximity sensor returns a positive result so that the device does not lock while in
+ * a phone call. This function only controls whether the device will go to sleep or dream which
+ * is independent of whether it will be allowed to suspend.
*/
- private boolean isBeingKeptAwakeLocked() {
+ private boolean isBeingKeptAwakeLocked(int groupId) {
return mStayOn
|| mProximityPositive
- || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0
- || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
- | USER_ACTIVITY_SCREEN_DIM)) != 0
+ || (mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId)
+ & WAKE_LOCK_STAY_AWAKE) != 0
+ || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId) & (
+ USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0
|| mScreenBrightnessBoostInProgress;
}
/**
- * Returns true if the device is prevented from going into inattentive sleep by the stay on
- * while powered setting. We also keep the device awake when the proximity sensor returns a
- * positive result so that the device does not lock while in a phone call. This function only
- * controls whether the device will go to sleep which is independent of whether it will be
- * allowed to suspend.
+ * Returns true if the DisplayGroup with the provided {@code groupId} is prevented from going
+ * into inattentive sleep by the stay on while powered setting. We also keep the device awake
+ * when the proximity sensor returns a positive result so that the device does not lock while in
+ * a phone call. This function only controls whether the device will go to sleep which is
+ * independent of whether it will be allowed to suspend.
*/
- private boolean isBeingKeptFromInattentiveSleepLocked() {
+ private boolean isBeingKeptFromInattentiveSleepLocked(int groupId) {
return mStayOn || mScreenBrightnessBoostInProgress || mProximityPositive
- || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
- | USER_ACTIVITY_SCREEN_DIM)) != 0;
+ || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId) & (
+ USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0;
}
private boolean isBeingKeptFromShowingInattentiveSleepWarningLocked() {
@@ -2902,7 +3010,7 @@
if (mDreamsBatteryLevelDrainCutoffConfig >= 0
&& mBatteryLevel < mBatteryLevelWhenDreamStarted
- mDreamsBatteryLevelDrainCutoffConfig
- && !isBeingKeptAwakeLocked()) {
+ && !isBeingKeptAwakeLocked(groupId)) {
// If the user activity timeout expired and the battery appears
// to be draining faster than it is charging then stop dreaming
// and go to sleep.
@@ -2917,18 +3025,17 @@
}
// Dream has ended or will be stopped. Update the power state.
- if (isItBedTimeYetLocked()) {
- final int flags = isAttentiveTimeoutExpired(now)
+ if (isItBedTimeYetLocked(groupId)) {
+ final int flags = isAttentiveTimeoutExpired(groupId, now)
? PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE : 0;
sleepDisplayGroupNoUpdateLocked(groupId, now,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, Process.SYSTEM_UID);
- updatePowerStateLocked();
} else {
wakeDisplayGroupNoUpdateLocked(groupId, now, PowerManager.WAKE_REASON_UNKNOWN,
"android.server.power:DREAM_FINISHED", Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
- updatePowerStateLocked();
}
+ updatePowerStateLocked();
} else if (wakefulness == WAKEFULNESS_DOZING) {
if (isDreaming) {
return; // continue dozing
@@ -2952,17 +3059,18 @@
private boolean canDreamLocked(int groupId) {
final DisplayPowerRequest displayPowerRequest =
mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId);
- if (getWakefulnessLocked() != WAKEFULNESS_DREAMING
+ if (!mBootCompleted
+ || getWakefulnessLocked() != WAKEFULNESS_DREAMING
|| !mDreamsSupportedConfig
|| !mDreamsEnabledSetting
|| !displayPowerRequest.isBrightOrDim()
|| displayPowerRequest.isVr()
- || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
- | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0
- || !mBootCompleted) {
+ || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId) & (
+ USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM
+ | USER_ACTIVITY_SCREEN_DREAM)) == 0) {
return false;
}
- if (!isBeingKeptAwakeLocked()) {
+ if (!isBeingKeptAwakeLocked(groupId)) {
if (!mIsPowered && !mDreamsEnabledOnBatteryConfig) {
return false;
}
@@ -2971,11 +3079,9 @@
&& mBatteryLevel < mDreamsBatteryLevelMinimumWhenNotPoweredConfig) {
return false;
}
- if (mIsPowered
- && mDreamsBatteryLevelMinimumWhenPoweredConfig >= 0
- && mBatteryLevel < mDreamsBatteryLevelMinimumWhenPoweredConfig) {
- return false;
- }
+ return !mIsPowered
+ || mDreamsBatteryLevelMinimumWhenPoweredConfig < 0
+ || mBatteryLevel >= mDreamsBatteryLevelMinimumWhenPoweredConfig;
}
return true;
}
@@ -3002,7 +3108,7 @@
if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED |
- DIRTY_QUIESCENT | DIRTY_DISPLAY_GROUP_POWER_UPDATED)) != 0) {
+ DIRTY_QUIESCENT | DIRTY_DISPLAY_GROUP_WAKEFULNESS)) != 0) {
if ((dirty & DIRTY_QUIESCENT) != 0) {
if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
sQuiescent = false;
@@ -3043,8 +3149,8 @@
if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
- if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0
- && !mDrawWakeLockOverrideFromSidekick) {
+ if ((mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId)
+ & WAKE_LOCK_DRAW) != 0 && !mDrawWakeLockOverrideFromSidekick) {
if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
displayPowerRequest.dozeScreenState = Display.STATE_DOZE;
}
@@ -3070,9 +3176,10 @@
+ ", mWakefulness="
+ PowerManagerInternal.wakefulnessToString(
mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId))
- + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
+ + ", mWakeLockSummary=0x" + Integer.toHexString(
+ mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId))
+ ", mUserActivitySummary=0x" + Integer.toHexString(
- mUserActivitySummary)
+ mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId))
+ ", mBootCompleted=" + mBootCompleted
+ ", screenBrightnessOverride="
+ displayPowerRequest.screenBrightnessOverride
@@ -3135,10 +3242,11 @@
@VisibleForTesting
int getDesiredScreenPolicyLocked(int groupId) {
final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
+ final int wakeLockSummary = mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(groupId);
if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
return DisplayPowerRequest.POLICY_OFF;
} else if (wakefulness == WAKEFULNESS_DOZING) {
- if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
+ if ((wakeLockSummary & WAKE_LOCK_DOZE) != 0) {
return DisplayPowerRequest.POLICY_DOZE;
}
if (mDozeAfterScreenOff) {
@@ -3155,9 +3263,10 @@
return DisplayPowerRequest.POLICY_VR;
}
- if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
- || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+ if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
|| !mBootCompleted
+ || (mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(groupId)
+ & USER_ACTIVITY_SCREEN_BRIGHT) != 0
|| mScreenBrightnessBoostInProgress) {
return DisplayPowerRequest.POLICY_BRIGHT;
}
@@ -3190,7 +3299,7 @@
synchronized (mLock) {
mProximityPositive = false;
mDirty |= DIRTY_PROXIMITY_POSITIVE;
- userActivityNoUpdateLocked(mClock.uptimeMillis(),
+ userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
}
@@ -3233,7 +3342,9 @@
};
private boolean shouldUseProximitySensorLocked() {
- return !mIsVrModeEnabled && (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0;
+ // Use default display group for proximity sensor.
+ return !mIsVrModeEnabled && (mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(
+ Display.DEFAULT_DISPLAY_GROUP) & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0;
}
/**
@@ -3758,7 +3869,7 @@
mScreenBrightnessBoostInProgress = true;
mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;
- userActivityNoUpdateLocked(eventTime,
+ userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, eventTime,
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
updatePowerStateLocked();
}
@@ -3863,14 +3974,16 @@
@VisibleForTesting
boolean wasDeviceIdleForInternal(long ms) {
synchronized (mLock) {
- return mLastUserActivityTime + ms < mClock.uptimeMillis();
+ return mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(
+ Display.DEFAULT_DISPLAY_GROUP) + ms < mClock.uptimeMillis();
}
}
@VisibleForTesting
void onUserActivity() {
synchronized (mLock) {
- mLastUserActivityTime = mClock.uptimeMillis();
+ mDisplayGroupPowerStateMapper.setLastUserActivityTimeLocked(
+ Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis());
}
}
@@ -4024,7 +4137,6 @@
TimeUtils.formatDuration(mNotifyLongNextCheck, mClock.uptimeMillis(), pw);
}
pw.println();
- pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
pw.println(" mSandmanScheduled=" + mSandmanScheduled);
pw.println(" mBatteryLevelLow=" + mBatteryLevelLow);
@@ -4035,9 +4147,6 @@
pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime));
pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime));
pw.println(" mLastSleepReason=" + PowerManager.sleepReasonToString(mLastSleepReason));
- pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime));
- pw.println(" mLastUserActivityTimeNoChangeLights="
- + TimeUtils.formatUptime(mLastUserActivityTimeNoChangeLights));
pw.println(" mLastInteractivePowerHintTime="
+ TimeUtils.formatUptime(mLastInteractivePowerHintTime));
pw.println(" mLastScreenBrightnessBoostTime="
@@ -4181,6 +4290,20 @@
pw.println(profile.mLockingNotified);
}
+ pw.println("Display Group User Activity:");
+ for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ pw.println(" displayGroupId=" + id);
+ pw.println(" userActivitySummary=0x" + Integer.toHexString(
+ mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(id)));
+ pw.println(" lastUserActivityTime=" + TimeUtils.formatUptime(
+ mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(id)));
+ pw.println(" lastUserActivityTimeNoChangeLights=" + TimeUtils.formatUptime(
+ mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
+ id)));
+ pw.println(" mWakeLockSummary=0x" + Integer.toHexString(
+ mDisplayGroupPowerStateMapper.getWakeLockSummaryLocked(id)));
+ }
+
wcd = mWirelessChargerDetector;
}
@@ -4266,17 +4389,27 @@
proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_DISPATCHED_MS, mNotifyLongDispatched);
proto.write(PowerManagerServiceDumpProto.NOTIFY_LONG_NEXT_CHECK_MS, mNotifyLongNextCheck);
- final long userActivityToken = proto.start(PowerManagerServiceDumpProto.USER_ACTIVITY);
- proto.write(
- PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
- (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0);
- proto.write(
- PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
- (mUserActivitySummary & USER_ACTIVITY_SCREEN_DIM) != 0);
- proto.write(
- PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DREAM,
- (mUserActivitySummary & USER_ACTIVITY_SCREEN_DREAM) != 0);
- proto.end(userActivityToken);
+ for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) {
+ final long userActivityToken = proto.start(
+ PowerManagerServiceDumpProto.USER_ACTIVITY);
+ proto.write(PowerManagerServiceDumpProto.UserActivityProto.DISPLAY_GROUP_ID, id);
+ final long userActivitySummary =
+ mDisplayGroupPowerStateMapper.getUserActivitySummaryLocked(id);
+ proto.write(PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_BRIGHT,
+ (userActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0);
+ proto.write(PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DIM,
+ (userActivitySummary & USER_ACTIVITY_SCREEN_DIM) != 0);
+ proto.write(PowerManagerServiceDumpProto.UserActivityProto.IS_SCREEN_DREAM,
+ (userActivitySummary & USER_ACTIVITY_SCREEN_DREAM) != 0);
+ proto.write(
+ PowerManagerServiceDumpProto.UserActivityProto.LAST_USER_ACTIVITY_TIME_MS,
+ mDisplayGroupPowerStateMapper.getLastUserActivityTimeLocked(id));
+ proto.write(
+ PowerManagerServiceDumpProto.UserActivityProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
+ mDisplayGroupPowerStateMapper.getLastUserActivityTimeNoChangeLightsLocked(
+ id));
+ proto.end(userActivityToken);
+ }
proto.write(
PowerManagerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY,
@@ -4295,10 +4428,6 @@
proto.write(PowerManagerServiceDumpProto.LAST_WAKE_TIME_MS, mLastWakeTime);
proto.write(PowerManagerServiceDumpProto.LAST_SLEEP_TIME_MS, mLastSleepTime);
- proto.write(PowerManagerServiceDumpProto.LAST_USER_ACTIVITY_TIME_MS, mLastUserActivityTime);
- proto.write(
- PowerManagerServiceDumpProto.LAST_USER_ACTIVITY_TIME_NO_CHANGE_LIGHTS_MS,
- mLastUserActivityTimeNoChangeLights);
proto.write(
PowerManagerServiceDumpProto.LAST_INTERACTIVE_POWER_HINT_TIME_MS,
mLastInteractivePowerHintTime);
@@ -4663,6 +4792,7 @@
*/
/* package */ final class WakeLock implements IBinder.DeathRecipient {
public final IBinder mLock;
+ public final int mDisplayId;
public int mFlags;
public String mTag;
public final String mPackageName;
@@ -4676,10 +4806,11 @@
public boolean mNotifiedLong;
public boolean mDisabled;
- public WakeLock(IBinder lock, int flags, String tag, String packageName,
+ public WakeLock(IBinder lock, int displayId, int flags, String tag, String packageName,
WorkSource workSource, String historyTag, int ownerUid, int ownerPid,
UidState uidState) {
mLock = lock;
+ mDisplayId = displayId;
mFlags = flags;
mTag = tag;
mPackageName = packageName;
@@ -4732,6 +4863,15 @@
mWorkSource = copyWorkSource(workSource);
}
+ public int getDisplayGroupId() {
+ if (!mSystemReady || mDisplayId == Display.INVALID_DISPLAY) {
+ return Display.INVALID_DISPLAY_GROUP;
+ }
+
+ final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
+ return displayInfo == null ? Display.INVALID_DISPLAY_GROUP : displayInfo.displayGroupId;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -4921,11 +5061,11 @@
@Override // Binder call
public void acquireWakeLockWithUid(IBinder lock, int flags, String tag,
- String packageName, int uid) {
+ String packageName, int uid, int displayId) {
if (uid < 0) {
uid = Binder.getCallingUid();
}
- acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null);
+ acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null, displayId);
}
@Override // Binder call
@@ -4960,7 +5100,7 @@
@Override // Binder call
public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName,
- WorkSource ws, String historyTag) {
+ WorkSource ws, String historyTag, int displayId) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
@@ -4985,7 +5125,8 @@
final int pid = Binder.getCallingPid();
final long ident = Binder.clearCallingIdentity();
try {
- acquireWakeLockInternal(lock, flags, tag, packageName, ws, historyTag, uid, pid);
+ acquireWakeLockInternal(lock, displayId, flags, tag, packageName, ws, historyTag,
+ uid, pid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -4994,7 +5135,7 @@
@Override // Binder call
public void acquireWakeLockAsync(IBinder lock, int flags, String tag, String packageName,
WorkSource ws, String historyTag) {
- acquireWakeLock(lock, flags, tag, packageName, ws, historyTag);
+ acquireWakeLock(lock, flags, tag, packageName, ws, historyTag, Display.INVALID_DISPLAY);
}
@Override // Binder call
@@ -5102,7 +5243,7 @@
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- userActivityInternal(eventTime, event, flags, uid);
+ userActivityInternal(displayId, eventTime, event, flags, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index a4459d0..4adcfb6 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -615,7 +615,7 @@
if (session == null || session.isStagedSessionFailed()) {
iter.remove();
deleteRollback(rollback,
- "Session " + session.getSessionId() + " not existed or failed");
+ "Session " + rollback.getStagedSessionId() + " not existed or failed");
continue;
}
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 3f20302..9c8ff68 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -18,12 +18,12 @@
import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.speech.IRecognitionListener;
@@ -40,10 +40,6 @@
private static final String TAG = RemoteSpeechRecognitionService.class.getSimpleName();
private static final boolean DEBUG = false;
- private static final String APP_OP_MESSAGE = "Recording audio for speech recognition";
- private static final String RECORD_AUDIO_APP_OP =
- AppOpsManager.permissionToOp(android.Manifest.permission.RECORD_AUDIO);
-
private final Object mLock = new Object();
private boolean mConnected = false;
@@ -53,14 +49,6 @@
@Nullable
@GuardedBy("mLock")
- private String mPackageName;
-
- @Nullable
- @GuardedBy("mLock")
- private String mFeatureId;
-
- @Nullable
- @GuardedBy("mLock")
private DelegatingListener mDelegatingListener;
// Makes sure we can block startListening() if session is still in progress.
@@ -72,7 +60,6 @@
private boolean mRecordingInProgress = false;
private final int mCallingUid;
- private final AppOpsManager mAppOpsManager;
private final ComponentName mComponentName;
RemoteSpeechRecognitionService(
@@ -87,7 +74,6 @@
IRecognitionService.Stub::asInterface);
mCallingUid = callingUid;
- mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mComponentName = serviceName;
if (DEBUG) {
@@ -99,11 +85,12 @@
return mComponentName;
}
- void startListening(Intent recognizerIntent, IRecognitionListener listener, String packageName,
- String featureId) {
+ void startListening(Intent recognizerIntent, IRecognitionListener listener,
+ @NonNull AttributionSource attributionSource) {
if (DEBUG) {
Slog.i(TAG, String.format("#startListening for package: %s, feature=%s, callingUid=%d",
- packageName, featureId, mCallingUid));
+ attributionSource.getPackageName(), attributionSource.getAttributionTag(),
+ mCallingUid));
}
if (listener == null) {
@@ -123,10 +110,6 @@
return;
}
- if (startProxyOp(packageName, featureId) != AppOpsManager.MODE_ALLOWED) {
- tryRespondWithError(listener, SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
- return;
- }
mSessionInProgress = true;
mRecordingInProgress = true;
@@ -141,23 +124,18 @@
resetStateLocked();
}
});
- mPackageName = packageName;
- mFeatureId = featureId;
run(service ->
service.startListening(
recognizerIntent,
mDelegatingListener,
- packageName,
- featureId,
- mCallingUid));
+ attributionSource));
}
}
- void stopListening(
- IRecognitionListener listener, String packageName, String featureId) {
+ void stopListening(IRecognitionListener listener) {
if (DEBUG) {
- Slog.i(TAG, "#stopListening for package: " + packageName + ", feature=" + featureId);
+ Slog.i(TAG, "#stopListening");
}
if (!mConnected) {
@@ -184,19 +162,13 @@
}
mRecordingInProgress = false;
- finishProxyOp(packageName, featureId);
-
- run(service -> service.stopListening(mDelegatingListener, packageName, featureId));
+ run(service -> service.stopListening(mDelegatingListener));
}
}
- void cancel(
- IRecognitionListener listener,
- String packageName,
- String featureId,
- boolean isShutdown) {
+ void cancel(IRecognitionListener listener, boolean isShutdown) {
if (DEBUG) {
- Slog.i(TAG, "#cancel for package: " + packageName + ", feature=" + featureId);
+ Slog.i(TAG, "#cancel");
}
if (!mConnected) {
@@ -220,11 +192,8 @@
// Temporary reference to allow for resetting the hard link mDelegatingListener to null.
IRecognitionListener delegatingListener = mDelegatingListener;
- run(service -> service.cancel(delegatingListener, packageName, featureId, isShutdown));
+ run(service -> service.cancel(delegatingListener, isShutdown));
- if (mRecordingInProgress) {
- finishProxyOp(packageName, featureId);
- }
mRecordingInProgress = false;
mSessionInProgress = false;
@@ -249,7 +218,7 @@
}
}
- cancel(mListener, mPackageName, mFeatureId, true /* isShutdown */);
+ cancel(mListener, true /* isShutdown */);
}
@Override // from ServiceConnector.Impl
@@ -286,40 +255,12 @@
}
private void resetStateLocked() {
- if (mRecordingInProgress && mPackageName != null) {
- finishProxyOp(mPackageName, mFeatureId);
- }
-
mListener = null;
mDelegatingListener = null;
mSessionInProgress = false;
mRecordingInProgress = false;
}
- private int startProxyOp(String packageName, String featureId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mAppOpsManager.startProxyOp(
- RECORD_AUDIO_APP_OP,
- mCallingUid,
- packageName,
- featureId,
- APP_OP_MESSAGE);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private void finishProxyOp(String packageName, String featureId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- mAppOpsManager.finishProxyOp(
- RECORD_AUDIO_APP_OP, mCallingUid, packageName, featureId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
private static void tryRespondWithError(IRecognitionListener listener, int errorCode) {
if (DEBUG) {
Slog.i(TAG, "Responding with error " + errorCode);
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
index 52c1467b..22eeb34 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
@@ -75,7 +75,7 @@
@Override
protected SpeechRecognitionManagerServiceImpl newServiceLocked(
@UserIdInt int resolvedUserId, boolean disabled) {
- return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId, disabled);
+ return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId);
}
final class SpeechRecognitionManagerServiceStub extends IRecognitionServiceManager.Stub {
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
index 769e049..eee08c2 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -36,8 +37,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractPerUserSystemService;
-import com.google.android.collect.Sets;
-
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -60,7 +59,7 @@
SpeechRecognitionManagerServiceImpl(
@NonNull SpeechRecognitionManagerService master,
- @NonNull Object lock, @UserIdInt int userId, boolean disabled) {
+ @NonNull Object lock, @UserIdInt int userId) {
super(master, lock, userId);
}
@@ -108,10 +107,6 @@
}
final int creatorCallingUid = Binder.getCallingUid();
- Set<String> creatorPackageNames =
- Sets.newArraySet(
- getContext().getPackageManager().getPackagesForUid(creatorCallingUid));
-
RemoteSpeechRecognitionService service = createService(creatorCallingUid, serviceComponent);
if (service == null) {
@@ -127,6 +122,7 @@
} catch (RemoteException e) {
// RemoteException == binder already died, schedule disconnect anyway.
handleClientDeath(creatorCallingUid, service, true /* invoke #cancel */);
+ return;
}
service.connect().thenAccept(binderService -> {
@@ -137,41 +133,24 @@
public void startListening(
Intent recognizerIntent,
IRecognitionListener listener,
- String packageName,
- String featureId,
- int callingUid) throws RemoteException {
- verifyCallerIdentity(
- creatorCallingUid, packageName, creatorPackageNames, listener);
- if (callingUid != creatorCallingUid) {
- listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
- return;
- }
-
- service.startListening(
- recognizerIntent, listener, packageName, featureId);
+ @NonNull AttributionSource attributionSource)
+ throws RemoteException {
+ attributionSource.enforceCallingUid();
+ service.startListening(recognizerIntent, listener, attributionSource);
}
@Override
public void stopListening(
- IRecognitionListener listener,
- String packageName,
- String featureId) throws RemoteException {
- verifyCallerIdentity(
- creatorCallingUid, packageName, creatorPackageNames, listener);
-
- service.stopListening(listener, packageName, featureId);
+ IRecognitionListener listener) throws RemoteException {
+ service.stopListening(listener);
}
@Override
public void cancel(
IRecognitionListener listener,
- String packageName,
- String featureId,
boolean isShutdown) throws RemoteException {
- verifyCallerIdentity(
- creatorCallingUid, packageName, creatorPackageNames, listener);
- service.cancel(listener, packageName, featureId, isShutdown);
+ service.cancel(listener, isShutdown);
if (isShutdown) {
handleClientDeath(
@@ -192,17 +171,6 @@
});
}
- private void verifyCallerIdentity(
- int creatorCallingUid,
- String packageName,
- Set<String> creatorPackageNames,
- IRecognitionListener listener) throws RemoteException {
- if (creatorCallingUid != Binder.getCallingUid()
- || !creatorPackageNames.contains(packageName)) {
- listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
- }
- }
-
private void handleClientDeath(
int callingUid,
RemoteSpeechRecognitionService service, boolean invokeCancel) {
diff --git a/services/core/java/com/android/server/stats/StatsHelper.java b/services/core/java/com/android/server/stats/StatsHelper.java
new file mode 100644
index 0000000..9b9f6b50
--- /dev/null
+++ b/services/core/java/com/android/server/stats/StatsHelper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.stats;
+
+import static android.app.StatsManager.ACTION_STATSD_STARTED;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+
+/**
+ * Provides helper methods for the Statsd APEX
+ *
+ * @hide
+ **/
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public final class StatsHelper {
+ private StatsHelper() {}
+
+ /**
+ * Send statsd ready broadcast
+ *
+ **/
+ public static void sendStatsdReadyBroadcast(@NonNull final Context context) {
+ context.sendBroadcastAsUser(
+ new Intent(ACTION_STATSD_STARTED).addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
+ UserHandle.SYSTEM, android.Manifest.permission.DUMP);
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java b/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java
index 953837a..7f2b07b 100644
--- a/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java
+++ b/services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import android.hardware.vibrator.IVibrator;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
import android.os.vibrator.RampSegment;
@@ -30,25 +31,31 @@
/** Adapts a {@link VibrationEffect} to a specific device, taking into account its capabilities. */
final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<VibratorInfo> {
- /**
- * Adapts a sequence of {@link VibrationEffectSegment} to device's absolute frequency values
- * and respective supported amplitudes.
- *
- * <p>This adapter preserves the segment count.
- */
- interface AmplitudeFrequencyAdapter {
- List<VibrationEffectSegment> apply(List<VibrationEffectSegment> segments,
- VibratorInfo info);
+ /** Adapts a sequence of {@link VibrationEffectSegment} to device's capabilities. */
+ interface SegmentsAdapter {
+
+ /**
+ * Modifies the given segments list by adding/removing segments to it based on the
+ * device capabilities specified by given {@link VibratorInfo}.
+ *
+ * @param segments List of {@link VibrationEffectSegment} to be adapter to the device.
+ * @param repeatIndex Repeat index on the current segment list.
+ * @param info The device vibrator info that the segments must be adapted to.
+ * @return The new repeat index on the modifies list.
+ */
+ int apply(List<VibrationEffectSegment> segments, int repeatIndex, VibratorInfo info);
}
- private final AmplitudeFrequencyAdapter mAmplitudeFrequencyAdapter;
+ private final SegmentsAdapter mAmplitudeFrequencyAdapter;
+ private final SegmentsAdapter mStepToRampAdapter;
DeviceVibrationEffectAdapter() {
this(new ClippingAmplitudeFrequencyAdapter());
}
- DeviceVibrationEffectAdapter(AmplitudeFrequencyAdapter amplitudeFrequencyAdapter) {
+ DeviceVibrationEffectAdapter(SegmentsAdapter amplitudeFrequencyAdapter) {
mAmplitudeFrequencyAdapter = amplitudeFrequencyAdapter;
+ mStepToRampAdapter = new StepToRampAdapter();
}
@Override
@@ -58,14 +65,62 @@
}
VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
- List<VibrationEffectSegment> mappedSegments = mAmplitudeFrequencyAdapter.apply(
- composed.getSegments(), info);
+ List<VibrationEffectSegment> newSegments = new ArrayList<>(composed.getSegments());
+ int newRepeatIndex = composed.getRepeatIndex();
- // TODO(b/167947076): add ramp to step adapter once PWLE capability is introduced
+ // Maps steps that should be handled by PWLE to ramps.
+ // This should be done before frequency is converted from relative to absolute values.
+ newRepeatIndex = mStepToRampAdapter.apply(newSegments, newRepeatIndex, info);
+
+ // Adapt amplitude and frequency values to device supported ones, converting frequency
+ // to absolute values in Hertz.
+ newRepeatIndex = mAmplitudeFrequencyAdapter.apply(newSegments, newRepeatIndex, info);
+
+ // TODO(b/167947076): add ramp to step adapter
// TODO(b/167947076): add filter that removes unsupported primitives
// TODO(b/167947076): add filter that replaces unsupported prebaked with fallback
- return new VibrationEffect.Composed(mappedSegments, composed.getRepeatIndex());
+ return new VibrationEffect.Composed(newSegments, newRepeatIndex);
+ }
+
+ /**
+ * Adapter that converts step segments that should be handled as PWLEs to ramp segments.
+ *
+ * <p>This leves the list unchanged if the device do not have compose PWLE capability.
+ */
+ private static final class StepToRampAdapter implements SegmentsAdapter {
+ @Override
+ public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
+ VibratorInfo info) {
+ if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
+ // The vibrator do not have PWLE capability, so keep the segments unchanged.
+ return repeatIndex;
+ }
+ int segmentCount = segments.size();
+ // Convert steps that require frequency control to ramps.
+ for (int i = 0; i < segmentCount; i++) {
+ VibrationEffectSegment segment = segments.get(i);
+ if ((segment instanceof StepSegment)
+ && ((StepSegment) segment).getFrequency() != 0) {
+ segments.set(i, apply((StepSegment) segment));
+ }
+ }
+ // Convert steps that are next to ramps to also become ramps, so they can be composed
+ // together in the same PWLE waveform.
+ for (int i = 1; i < segmentCount; i++) {
+ if (segments.get(i) instanceof RampSegment) {
+ for (int j = i - 1; j >= 0 && (segments.get(j) instanceof StepSegment); j--) {
+ segments.set(j, apply((StepSegment) segments.get(j)));
+ }
+ }
+ }
+ return repeatIndex;
+ }
+
+ private RampSegment apply(StepSegment segment) {
+ return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
+ segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
+ }
}
/**
@@ -74,25 +129,24 @@
*
* <p>Devices with no frequency control will collapse all frequencies to zero and leave
* amplitudes unchanged.
+ *
+ * <p>The frequency value returned in segments will be absolute, conveted with
+ * {@link VibratorInfo#getAbsoluteFrequency(float)}.
*/
- private static final class ClippingAmplitudeFrequencyAdapter
- implements AmplitudeFrequencyAdapter {
+ private static final class ClippingAmplitudeFrequencyAdapter implements SegmentsAdapter {
@Override
- public List<VibrationEffectSegment> apply(List<VibrationEffectSegment> segments,
+ public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
VibratorInfo info) {
- List<VibrationEffectSegment> result = new ArrayList<>();
int segmentCount = segments.size();
for (int i = 0; i < segmentCount; i++) {
VibrationEffectSegment segment = segments.get(i);
if (segment instanceof StepSegment) {
- result.add(apply((StepSegment) segment, info));
+ segments.set(i, apply((StepSegment) segment, info));
} else if (segment instanceof RampSegment) {
- result.add(apply((RampSegment) segment, info));
- } else {
- result.add(segment);
+ segments.set(i, apply((RampSegment) segment, info));
}
}
- return result;
+ return repeatIndex;
}
private StepSegment apply(StepSegment segment, VibratorInfo info) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 3090e6d..df85e26 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -252,36 +252,38 @@
}
}
- /**
- * Get the duration the vibrator will be on for given {@code waveform}, starting at {@code
- * startIndex} until the next time it's vibrating amplitude is zero.
- */
- private static long getVibratorOnDuration(VibrationEffect.Composed effect, int startIndex) {
- List<VibrationEffectSegment> segments = effect.getSegments();
- int segmentCount = segments.size();
- int repeatIndex = effect.getRepeatIndex();
- int i = startIndex;
- long timing = 0;
- while (i < segmentCount) {
- if (!(segments.get(i) instanceof StepSegment)) {
- break;
- }
- StepSegment stepSegment = (StepSegment) segments.get(i);
- if (stepSegment.getAmplitude() == 0) {
- break;
- }
- timing += stepSegment.getDuration();
- i++;
- if (i == segmentCount && repeatIndex >= 0) {
- i = repeatIndex;
- // prevent infinite loop
- repeatIndex = -1;
- }
- if (i == startIndex) {
- return 1000;
+ @Nullable
+ private SingleVibratorStep nextVibrateStep(long startTime, VibratorController controller,
+ VibrationEffect.Composed effect, int segmentIndex, long vibratorOffTimeout) {
+ // Some steps should only start after the vibrator has finished the previous vibration, so
+ // make sure we take the latest between both timings.
+ long latestStartTime = Math.max(startTime, vibratorOffTimeout);
+ if (segmentIndex >= effect.getSegments().size()) {
+ segmentIndex = effect.getRepeatIndex();
+ }
+ if (segmentIndex < 0) {
+ if (vibratorOffTimeout > SystemClock.uptimeMillis()) {
+ // No more segments to play, last step is to wait for the vibrator to complete
+ return new OffStep(vibratorOffTimeout, controller);
+ } else {
+ return null;
}
}
- return timing;
+
+ VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
+ if (segment instanceof PrebakedSegment) {
+ return new PerformStep(latestStartTime, controller, effect, segmentIndex,
+ vibratorOffTimeout);
+ }
+ if (segment instanceof PrimitiveSegment) {
+ return new ComposePrimitivesStep(latestStartTime, controller, effect, segmentIndex,
+ vibratorOffTimeout);
+ }
+ if (segment instanceof RampSegment) {
+ return new ComposePwleStep(latestStartTime, controller, effect, segmentIndex,
+ vibratorOffTimeout);
+ }
+ return new AmplitudeStep(startTime, controller, effect, segmentIndex, vibratorOffTimeout);
}
private static CombinedVibrationEffect.Sequential toSequential(CombinedVibrationEffect effect) {
@@ -449,8 +451,13 @@
duration = startVibrating(effectMapping, nextSteps);
noteVibratorOn(duration);
} finally {
- // If this step triggered any vibrator then add a finish step to wait for all
+ if (duration < 0) {
+ // Something failed while playing this step so stop playing this sequence.
+ return EMPTY_STEP_LIST;
+ }
+ // It least one vibrator was started then add a finish step to wait for all
// active vibrators to finish their individual steps before going to the next.
+ // Otherwise this step was ignored so just go to the next one.
Step nextStep = duration > 0 ? new FinishVibrateStep(this) : nextStep();
if (nextStep != null) {
nextSteps.add(nextStep);
@@ -506,11 +513,13 @@
return 0;
}
- VibratorOnStep[] steps = new VibratorOnStep[vibratorCount];
+ SingleVibratorStep[] steps = new SingleVibratorStep[vibratorCount];
long vibrationStartTime = SystemClock.uptimeMillis();
for (int i = 0; i < vibratorCount; i++) {
- steps[i] = new VibratorOnStep(vibrationStartTime,
- mVibrators.get(effectMapping.vibratorIdAt(i)), effectMapping.effectAt(i));
+ steps[i] = nextVibrateStep(vibrationStartTime,
+ mVibrators.get(effectMapping.vibratorIdAt(i)),
+ effectMapping.effectAt(i),
+ /* segmentIndex= */ 0, /* vibratorOffTimeout= */ 0);
}
if (steps.length == 1) {
@@ -524,35 +533,52 @@
synchronized (mLock) {
boolean hasPrepared = false;
boolean hasTriggered = false;
+ long maxDuration = 0;
try {
hasPrepared = mCallbacks.prepareSyncedVibration(
effectMapping.getRequiredSyncCapabilities(),
effectMapping.getVibratorIds());
- long duration = 0;
- for (VibratorOnStep step : steps) {
- duration = Math.max(duration, startVibrating(step, nextSteps));
+ for (SingleVibratorStep step : steps) {
+ long duration = startVibrating(step, nextSteps);
+ if (duration < 0) {
+ // One vibrator has failed, fail this entire sync attempt.
+ return maxDuration = -1;
+ }
+ maxDuration = Math.max(maxDuration, duration);
}
// Check if sync was prepared and if any step was accepted by a vibrator,
// otherwise there is nothing to trigger here.
- if (hasPrepared && duration > 0) {
+ if (hasPrepared && maxDuration > 0) {
hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id);
}
- return duration;
+ return maxDuration;
} finally {
if (hasPrepared && !hasTriggered) {
// Trigger has failed or all steps were ignored by the vibrators.
mCallbacks.cancelSyncedVibration();
- return 0;
+ nextSteps.clear();
+ } else if (maxDuration < 0) {
+ // Some vibrator failed without being prepared so other vibrators might be
+ // active. Cancel and remove every pending step from output list.
+ for (int i = nextSteps.size() - 1; i >= 0; i--) {
+ nextSteps.remove(i).cancel();
+ }
}
}
}
}
- private long startVibrating(VibratorOnStep step, List<Step> nextSteps) {
+ private long startVibrating(SingleVibratorStep step, List<Step> nextSteps) {
nextSteps.addAll(step.play());
- return step.getDuration();
+ long stepDuration = step.getOnResult();
+ if (stepDuration < 0) {
+ // Step failed, so return negative duration to propagate failure.
+ return stepDuration;
+ }
+ // Return the longest estimation for the entire effect.
+ return Math.max(stepDuration, step.effect.getDuration());
}
}
@@ -592,121 +618,274 @@
}
/**
- * Represent a step turn the vibrator on.
- *
- * <p>No other calls to the vibrator is made from this step, so this can be played in between
- * calls to 'prepare' and 'trigger' for synchronized vibrations.
+ * Represent a step on a single vibrator that plays one or more segments from a
+ * {@link VibrationEffect.Composed} effect.
*/
- private final class VibratorOnStep extends Step {
+ private abstract class SingleVibratorStep extends Step {
public final VibratorController controller;
- public final VibrationEffect effect;
- private long mDuration;
+ public final VibrationEffect.Composed effect;
+ public final int segmentIndex;
+ public final long vibratorOffTimeout;
- VibratorOnStep(long startTime, VibratorController controller, VibrationEffect effect) {
+ long mVibratorOnResult;
+
+ /**
+ * @param startTime The time to schedule this step in the {@link StepQueue}.
+ * @param controller The vibrator that is playing the effect.
+ * @param effect The effect being played in this step.
+ * @param index The index of the next segment to be played by this step
+ * @param vibratorOffTimeout The time the vibrator is expected to complete any previous
+ * vibration and turn off. This is used to allow this step to be
+ * anticipated when the completion callback is triggered, and can
+ * be used play effects back-to-back.
+ */
+ SingleVibratorStep(long startTime, VibratorController controller,
+ VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
super(startTime);
this.controller = controller;
this.effect = effect;
+ this.segmentIndex = index;
+ this.vibratorOffTimeout = vibratorOffTimeout;
}
/**
- * Return the duration, in millis, of this effect. Repeating waveforms return {@link
- * Long#MAX_VALUE}. Zero or negative values indicate the vibrator has ignored this effect.
+ * Return the result a call to {@link VibratorController#on} method triggered by
+ * {@link #play()}.
+ *
+ * @return A positive duration that the vibrator was turned on for by this step;
+ * Zero if the segment is not supported or vibrator was never turned on;
+ * A negative value if the vibrator call has failed.
*/
- public long getDuration() {
- return mDuration;
+ public long getOnResult() {
+ return mVibratorOnResult;
+ }
+
+ @Override
+ public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
+ // Only anticipate this step if a timeout was set to wait for the vibration to complete,
+ // otherwise we are waiting for the correct time to play the next step.
+ return (controller.getVibratorInfo().getId() == vibratorId)
+ && (vibratorOffTimeout > SystemClock.uptimeMillis());
+ }
+
+ @Override
+ public void cancel() {
+ if (vibratorOffTimeout > SystemClock.uptimeMillis()) {
+ // Vibrator might be running from previous steps, so turn it off while canceling.
+ stopVibrating();
+ }
+ }
+
+ void stopVibrating() {
+ if (DEBUG) {
+ Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
+ }
+ controller.off();
+ }
+
+ /** Return the {@link #nextVibrateStep} with same timings, only jumping the segments. */
+ public List<Step> skipToNextSteps(int segmentsSkipped) {
+ return nextSteps(startTime, vibratorOffTimeout, segmentsSkipped);
+ }
+
+ /**
+ * Return the {@link #nextVibrateStep} with same start and off timings calculated from
+ * {@link #getOnResult()}, jumping all played segments.
+ *
+ * <p>This method has same behavior as {@link #skipToNextSteps(int)} when the vibrator
+ * result is non-positive, meaning the vibrator has either ignored or failed to turn on.
+ */
+ public List<Step> nextSteps(int segmentsPlayed) {
+ if (mVibratorOnResult <= 0) {
+ // Vibration was not started, so just skip the played segments and keep timings.
+ return skipToNextSteps(segmentsPlayed);
+ }
+ long nextVibratorOffTimeout =
+ SystemClock.uptimeMillis() + mVibratorOnResult + CALLBACKS_EXTRA_TIMEOUT;
+ return nextSteps(nextVibratorOffTimeout, nextVibratorOffTimeout, segmentsPlayed);
+ }
+
+ /**
+ * Return the {@link #nextVibrateStep} with given start and off timings, which might be
+ * calculated independently, jumping all played segments.
+ *
+ * <p>This should be used when the vibrator on/off state is not responsible for the steps
+ * execution timings, e.g. while playing the vibrator amplitudes.
+ */
+ public List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
+ int segmentsPlayed) {
+ Step nextStep = nextVibrateStep(nextStartTime, controller, effect,
+ segmentIndex + segmentsPlayed, vibratorOffTimeout);
+ return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep);
+ }
+ }
+
+ /**
+ * Represent a step turn the vibrator on with a single prebaked effect.
+ *
+ * <p>This step automatically falls back by replacing the prebaked segment with
+ * {@link VibrationSettings#getFallbackEffect(int)}, if available.
+ */
+ private final class PerformStep extends SingleVibratorStep {
+
+ PerformStep(long startTime, VibratorController controller,
+ VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
+ super(startTime, controller, effect, index, vibratorOffTimeout);
}
@Override
public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorOnStep");
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformStep");
try {
- if (DEBUG) {
- Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId());
+ VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
+ if (!(segment instanceof PrebakedSegment)) {
+ Slog.w(TAG, "Ignoring wrong segment for a PerformStep: " + segment);
+ return skipToNextSteps(/* segmentsSkipped= */ 1);
}
- List<Step> nextSteps = new ArrayList<>();
- mDuration = startVibrating(effect, nextSteps);
- return nextSteps;
+
+ PrebakedSegment prebaked = (PrebakedSegment) segment;
+ if (DEBUG) {
+ Slog.d(TAG, "Perform " + VibrationEffect.effectIdToString(
+ prebaked.getEffectId()) + " on vibrator "
+ + controller.getVibratorInfo().getId());
+ }
+
+ VibrationEffect fallback = mVibration.getFallback(prebaked.getEffectId());
+ mVibratorOnResult = controller.on(prebaked, mVibration.id);
+
+ if (mVibratorOnResult == 0 && prebaked.shouldFallback()
+ && (fallback instanceof VibrationEffect.Composed)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Playing fallback for effect "
+ + VibrationEffect.effectIdToString(prebaked.getEffectId()));
+ }
+ SingleVibratorStep fallbackStep = nextVibrateStep(startTime, controller,
+ replaceCurrentSegment((VibrationEffect.Composed) fallback),
+ segmentIndex, vibratorOffTimeout);
+ List<Step> fallbackResult = fallbackStep.play();
+ // Update the result with the fallback result so this step is seamlessly
+ // replaced by the fallback to any outer application of this.
+ mVibratorOnResult = fallbackStep.getOnResult();
+ return fallbackResult;
+ }
+
+ return nextSteps(/* segmentsPlayed= */ 1);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- private long startVibrating(VibrationEffect effect, List<Step> nextSteps) {
- // TODO(b/167947076): split this into 4 different step implementations:
- // VibratorPerformStep, VibratorComposePrimitiveStep, VibratorComposePwleStep and
- // VibratorAmplitudeStep.
- // Make sure each step carries over the full VibrationEffect and an incremental segment
- // index, and triggers a final VibratorOffStep once all segments are done.
- VibrationEffect.Composed composed = (VibrationEffect.Composed) effect;
- VibrationEffectSegment firstSegment = composed.getSegments().get(0);
- final long duration;
- final long now = SystemClock.uptimeMillis();
- if (firstSegment instanceof StepSegment) {
- // Return the full duration of this waveform effect.
- duration = effect.getDuration();
- long onDuration = getVibratorOnDuration(composed, 0);
- if (onDuration > 0) {
- // Do NOT set amplitude here. This might be called between prepareSynced and
- // triggerSynced, so the vibrator is not actually turned on here.
- // The next steps will handle the amplitudes after the vibrator has turned on.
- controller.on(onDuration, mVibration.id);
- }
- long offTime = onDuration > 0 ? now + onDuration + CALLBACKS_EXTRA_TIMEOUT : now;
- nextSteps.add(new VibratorAmplitudeStep(now, controller, composed, offTime));
- } else if (firstSegment instanceof PrebakedSegment) {
- PrebakedSegment prebaked = (PrebakedSegment) firstSegment;
- VibrationEffect fallback = mVibration.getFallback(prebaked.getEffectId());
- duration = controller.on(prebaked, mVibration.id);
- if (duration > 0) {
- nextSteps.add(new VibratorOffStep(now + duration + CALLBACKS_EXTRA_TIMEOUT,
- controller));
- } else if (prebaked.shouldFallback() && fallback != null) {
- return startVibrating(fallback, nextSteps);
- }
- } else if (firstSegment instanceof PrimitiveSegment) {
- int segmentCount = composed.getSegments().size();
- PrimitiveSegment[] primitives = new PrimitiveSegment[segmentCount];
- for (int i = 0; i < segmentCount; i++) {
- VibrationEffectSegment segment = composed.getSegments().get(i);
- if (segment instanceof PrimitiveSegment) {
- primitives[i] = (PrimitiveSegment) segment;
- } else {
- primitives[i] = new PrimitiveSegment(
- VibrationEffect.Composition.PRIMITIVE_NOOP,
- /* scale= */ 1, /* delay= */ 0);
- }
- }
- duration = controller.on(primitives, mVibration.id);
- if (duration > 0) {
- nextSteps.add(new VibratorOffStep(now + duration + CALLBACKS_EXTRA_TIMEOUT,
- controller));
- }
- } else if (firstSegment instanceof RampSegment) {
- int segmentCount = composed.getSegments().size();
- RampSegment[] primitives = new RampSegment[segmentCount];
- for (int i = 0; i < segmentCount; i++) {
- VibrationEffectSegment segment = composed.getSegments().get(i);
- if (segment instanceof RampSegment) {
- primitives[i] = (RampSegment) segment;
- } else if (segment instanceof StepSegment) {
- StepSegment stepSegment = (StepSegment) segment;
- primitives[i] = new RampSegment(
- stepSegment.getAmplitude(), stepSegment.getAmplitude(),
- stepSegment.getFrequency(), stepSegment.getFrequency(),
- (int) stepSegment.getDuration());
- } else {
- primitives[i] = new RampSegment(0, 0, 0, 0, 0);
- }
- }
- duration = controller.on(primitives, mVibration.id);
- if (duration > 0) {
- nextSteps.add(new VibratorOffStep(now + duration + CALLBACKS_EXTRA_TIMEOUT,
- controller));
- }
- } else {
- duration = 0;
+ /**
+ * Replace segment at {@link #segmentIndex} in {@link #effect} with given fallback segments.
+ *
+ * @return a copy of {@link #effect} with replaced segment.
+ */
+ private VibrationEffect.Composed replaceCurrentSegment(VibrationEffect.Composed fallback) {
+ List<VibrationEffectSegment> newSegments = new ArrayList<>(effect.getSegments());
+ int newRepeatIndex = effect.getRepeatIndex();
+ newSegments.remove(segmentIndex);
+ newSegments.addAll(segmentIndex, fallback.getSegments());
+ if (segmentIndex < effect.getRepeatIndex()) {
+ newRepeatIndex += fallback.getSegments().size();
}
- return duration;
+ return new VibrationEffect.Composed(newSegments, newRepeatIndex);
+ }
+ }
+
+ /**
+ * Represent a step turn the vibrator on using a composition of primitives.
+ *
+ * <p>This step will use the maximum supported number of consecutive segments of type
+ * {@link PrimitiveSegment} starting at the current index.
+ */
+ private final class ComposePrimitivesStep extends SingleVibratorStep {
+
+ ComposePrimitivesStep(long startTime, VibratorController controller,
+ VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
+ super(startTime, controller, effect, index, vibratorOffTimeout);
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
+ try {
+ int segmentCount = effect.getSegments().size();
+ List<PrimitiveSegment> primitives = new ArrayList<>();
+ for (int i = segmentIndex; i < segmentCount; i++) {
+ VibrationEffectSegment segment = effect.getSegments().get(i);
+ if (segment instanceof PrimitiveSegment) {
+ primitives.add((PrimitiveSegment) segment);
+ } else {
+ break;
+ }
+ }
+
+ if (primitives.isEmpty()) {
+ Slog.w(TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
+ + effect.getSegments().get(segmentIndex));
+ return skipToNextSteps(/* segmentsSkipped= */ 1);
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Compose " + primitives + " primitives on vibrator "
+ + controller.getVibratorInfo().getId());
+ }
+ mVibratorOnResult = controller.on(
+ primitives.toArray(new PrimitiveSegment[primitives.size()]),
+ mVibration.id);
+
+ return nextSteps(/* segmentsPlayed= */ primitives.size());
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+ }
+
+ /**
+ * Represent a step turn the vibrator on using a composition of PWLE segments.
+ *
+ * <p>This step will use the maximum supported number of consecutive segments of type
+ * {@link StepSegment} or {@link RampSegment} starting at the current index.
+ */
+ private final class ComposePwleStep extends SingleVibratorStep {
+
+ ComposePwleStep(long startTime, VibratorController controller,
+ VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
+ super(startTime, controller, effect, index, vibratorOffTimeout);
+ }
+
+ @Override
+ public List<Step> play() {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
+ try {
+ int segmentCount = effect.getSegments().size();
+ List<RampSegment> pwles = new ArrayList<>();
+ for (int i = segmentIndex; i < segmentCount; i++) {
+ VibrationEffectSegment segment = effect.getSegments().get(i);
+ if (segment instanceof RampSegment) {
+ pwles.add((RampSegment) segment);
+ } else {
+ break;
+ }
+ }
+
+ if (pwles.isEmpty()) {
+ Slog.w(TAG, "Ignoring wrong segment for a ComposePwleStep: "
+ + effect.getSegments().get(segmentIndex));
+ return skipToNextSteps(/* segmentsSkipped= */ 1);
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Compose " + pwles + " PWLEs on vibrator "
+ + controller.getVibratorInfo().getId());
+ }
+ mVibratorOnResult = controller.on(pwles.toArray(new RampSegment[pwles.size()]),
+ mVibration.id);
+
+ return nextSteps(/* segmentsPlayed= */ pwles.size());
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
}
}
@@ -716,22 +895,15 @@
* <p>This runs after a timeout on the expected time the vibrator should have finished playing,
* and can anticipated by vibrator complete callbacks.
*/
- private final class VibratorOffStep extends Step {
- public final VibratorController controller;
+ private final class OffStep extends SingleVibratorStep {
- VibratorOffStep(long startTime, VibratorController controller) {
- super(startTime);
- this.controller = controller;
- }
-
- @Override
- public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
- return controller.getVibratorInfo().getId() == vibratorId;
+ OffStep(long startTime, VibratorController controller) {
+ super(startTime, controller, /* effect= */ null, /* index= */ -1, startTime);
}
@Override
public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorOffStep");
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "OffStep");
try {
stopVibrating();
return EMPTY_STEP_LIST;
@@ -739,108 +911,88 @@
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
-
- @Override
- public void cancel() {
- stopVibrating();
- }
-
- private void stopVibrating() {
- if (DEBUG) {
- Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
- }
- controller.off();
- }
}
- /** Represents a step to change the amplitude of the vibrator. */
- private final class VibratorAmplitudeStep extends Step {
- public final VibratorController controller;
- public final VibrationEffect.Composed effect;
- public final int currentIndex;
+ /**
+ * Represents a step to turn the vibrator on and change its amplitude.
+ *
+ * <p>This step ignores vibration completion callbacks and control the vibrator on/off state
+ * and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}.
+ */
+ private final class AmplitudeStep extends SingleVibratorStep {
+ private long mNextOffTime;
- private long mNextVibratorStopTime;
-
- VibratorAmplitudeStep(long startTime, VibratorController controller,
- VibrationEffect.Composed effect, long expectedVibratorStopTime) {
- this(startTime, controller, effect, /* index= */ 0, expectedVibratorStopTime);
- }
-
- VibratorAmplitudeStep(long startTime, VibratorController controller,
- VibrationEffect.Composed effect, int index, long expectedVibratorStopTime) {
- super(startTime);
- this.controller = controller;
- this.effect = effect;
- this.currentIndex = index;
- mNextVibratorStopTime = expectedVibratorStopTime;
+ AmplitudeStep(long startTime, VibratorController controller,
+ VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
+ super(startTime, controller, effect, index, vibratorOffTimeout);
+ mNextOffTime = vibratorOffTimeout;
}
@Override
public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
if (controller.getVibratorInfo().getId() == vibratorId) {
- mNextVibratorStopTime = SystemClock.uptimeMillis();
+ mNextOffTime = SystemClock.uptimeMillis();
}
+ // Timings are tightly controlled here, so never anticipate when vibrator is complete.
return false;
}
@Override
public List<Step> play() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorAmplitudeStep");
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "AmplitudeStep");
try {
if (DEBUG) {
long latency = SystemClock.uptimeMillis() - startTime;
Slog.d(TAG, "Running amplitude step with " + latency + "ms latency.");
}
- VibrationEffectSegment segment = effect.getSegments().get(currentIndex);
+
+ VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
if (!(segment instanceof StepSegment)) {
- return nextSteps();
+ Slog.w(TAG, "Ignoring wrong segment for a AmplitudeStep: " + segment);
+ return skipToNextSteps(/* segmentsSkipped= */ 1);
}
+
StepSegment stepSegment = (StepSegment) segment;
if (stepSegment.getDuration() == 0) {
// Skip waveform entries with zero timing.
- return nextSteps();
+ return skipToNextSteps(/* segmentsSkipped= */ 1);
}
+
+ long now = SystemClock.uptimeMillis();
float amplitude = stepSegment.getAmplitude();
if (amplitude == 0) {
- stopVibrating();
- return nextSteps();
- }
- if (startTime >= mNextVibratorStopTime) {
- // Vibrator has stopped. Turn vibrator back on for the duration of another
- // cycle before setting the amplitude.
- long onDuration = getVibratorOnDuration(effect, currentIndex);
- if (onDuration > 0) {
- startVibrating(onDuration);
- mNextVibratorStopTime =
- SystemClock.uptimeMillis() + onDuration + CALLBACKS_EXTRA_TIMEOUT;
+ if (mNextOffTime > now) {
+ // Amplitude cannot be set to zero, so stop the vibrator.
+ stopVibrating();
+ mNextOffTime = now;
}
+ } else {
+ if (startTime >= mNextOffTime) {
+ // Vibrator has stopped. Turn vibrator back on for the duration of another
+ // cycle before setting the amplitude.
+ long onDuration = getVibratorOnDuration(effect, segmentIndex);
+ if (onDuration > 0) {
+ mVibratorOnResult = startVibrating(onDuration);
+ mNextOffTime = now + onDuration + CALLBACKS_EXTRA_TIMEOUT;
+ }
+ }
+ changeAmplitude(amplitude);
}
- changeAmplitude(amplitude);
- return nextSteps();
+
+ // Use original startTime to avoid propagating latencies to the waveform.
+ long nextStartTime = startTime + segment.getDuration();
+ return nextSteps(nextStartTime, mNextOffTime, /* segmentsPlayed= */ 1);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- @Override
- public void cancel() {
- stopVibrating();
- }
-
- private void stopVibrating() {
- if (DEBUG) {
- Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
- }
- controller.off();
- mNextVibratorStopTime = SystemClock.uptimeMillis();
- }
-
- private void startVibrating(long duration) {
+ private long startVibrating(long duration) {
if (DEBUG) {
Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
+ duration + "ms");
}
- controller.on(duration, mVibration.id);
+ return controller.on(duration, mVibration.id);
}
private void changeAmplitude(float amplitude) {
@@ -851,18 +1003,35 @@
controller.setAmplitude(amplitude);
}
- @NonNull
- private List<Step> nextSteps() {
- long nextStartTime = startTime + effect.getSegments().get(currentIndex).getDuration();
- int nextIndex = currentIndex + 1;
- if (nextIndex >= effect.getSegments().size()) {
- nextIndex = effect.getRepeatIndex();
+ /**
+ * Get the duration the vibrator will be on for a waveform, starting at {@code startIndex}
+ * until the next time it's vibrating amplitude is zero or a different type of segment is
+ * found.
+ */
+ private long getVibratorOnDuration(VibrationEffect.Composed effect, int startIndex) {
+ List<VibrationEffectSegment> segments = effect.getSegments();
+ int segmentCount = segments.size();
+ int repeatIndex = effect.getRepeatIndex();
+ int i = startIndex;
+ long timing = 0;
+ while (i < segmentCount) {
+ VibrationEffectSegment segment = segments.get(i);
+ if (!(segment instanceof StepSegment)
+ || ((StepSegment) segment).getAmplitude() == 0) {
+ break;
+ }
+ timing += segment.getDuration();
+ i++;
+ if (i == segmentCount && repeatIndex >= 0) {
+ i = repeatIndex;
+ // prevent infinite loop
+ repeatIndex = -1;
+ }
+ if (i == startIndex) {
+ return 1000;
+ }
}
- Step nextStep = nextIndex < 0
- ? new VibratorOffStep(nextStartTime, controller)
- : new VibratorAmplitudeStep(nextStartTime, controller, effect, nextIndex,
- mNextVibratorStopTime);
- return Arrays.asList(nextStep);
+ return timing;
}
}
@@ -873,7 +1042,7 @@
* play all of the effects in sync.
*/
private final class DeviceEffectMap {
- private final SparseArray<VibrationEffect> mVibratorEffects;
+ private final SparseArray<VibrationEffect.Composed> mVibratorEffects;
private final int[] mVibratorIds;
private final long mRequiredSyncCapabilities;
@@ -884,8 +1053,10 @@
int vibratorId = mVibrators.keyAt(i);
VibratorInfo vibratorInfo = mVibrators.valueAt(i).getVibratorInfo();
VibrationEffect effect = mDeviceEffectAdapter.apply(mono.getEffect(), vibratorInfo);
- mVibratorEffects.put(vibratorId, effect);
- mVibratorIds[i] = vibratorId;
+ if (effect instanceof VibrationEffect.Composed) {
+ mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
+ mVibratorIds[i] = vibratorId;
+ }
}
mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
}
@@ -899,7 +1070,9 @@
VibratorInfo vibratorInfo = mVibrators.valueAt(i).getVibratorInfo();
VibrationEffect effect = mDeviceEffectAdapter.apply(
stereoEffects.valueAt(i), vibratorInfo);
- mVibratorEffects.put(vibratorId, effect);
+ if (effect instanceof VibrationEffect.Composed) {
+ mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
+ }
}
}
mVibratorIds = new int[mVibratorEffects.size()];
@@ -937,7 +1110,7 @@
}
/** Return the {@link VibrationEffect} at given index. */
- public VibrationEffect effectAt(int index) {
+ public VibrationEffect.Composed effectAt(int index) {
return mVibratorEffects.valueAt(index);
}
@@ -948,11 +1121,11 @@
* @return {@link IVibratorManager#CAP_SYNC} together with all required
* IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
*/
- private long calculateRequiredSyncCapabilities(SparseArray<VibrationEffect> effects) {
+ private long calculateRequiredSyncCapabilities(
+ SparseArray<VibrationEffect.Composed> effects) {
long prepareCap = 0;
for (int i = 0; i < effects.size(); i++) {
- VibrationEffect.Composed composed = (VibrationEffect.Composed) effects.valueAt(i);
- VibrationEffectSegment firstSegment = composed.getSegments().get(0);
+ VibrationEffectSegment firstSegment = effects.valueAt(i).getSegments().get(0);
if (firstSegment instanceof StepSegment) {
prepareCap |= IVibratorManager.CAP_PREPARE_ON;
} else if (firstSegment instanceof PrebakedSegment) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index c897702..a09bfc5 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -30,6 +30,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import libcore.util.NativeAllocationRegistry;
@@ -65,13 +66,10 @@
NativeWrapper nativeWrapper) {
mNativeWrapper = nativeWrapper;
mNativeWrapper.init(vibratorId, listener);
-
- // TODO(b/167947076): load supported ones from HAL once API introduced
- VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
- Float.NaN, nativeWrapper.getResonantFrequency(), Float.NaN, Float.NaN, null);
- mVibratorInfo = new VibratorInfo(vibratorId, nativeWrapper.getCapabilities(),
- nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives(),
- nativeWrapper.getQFactor(), frequencyMapping);
+ // TODO(b/167947076): load suggested range from config
+ mVibratorInfo = mNativeWrapper.getInfo(/* suggestedFrequencyRange= */ 100);
+ Preconditions.checkNotNull(mVibratorInfo, "Failed to retrieve data for vibrator %d",
+ vibratorId);
}
/** Register state listener for this vibrator. */
@@ -189,11 +187,17 @@
* callback to {@link OnVibrationCompleteListener}.
*
* <p>This will affect the state of {@link #isVibrating()}.
+ *
+ * @return The positive duration of the vibration started, if successful, zero if the vibrator
+ * do not support the input or a negative number if the operation failed.
*/
- public void on(long milliseconds, long vibrationId) {
+ public long on(long milliseconds, long vibrationId) {
synchronized (mLock) {
- mNativeWrapper.on(milliseconds, vibrationId);
- notifyVibratorOnLocked();
+ long duration = mNativeWrapper.on(milliseconds, vibrationId);
+ if (duration > 0) {
+ notifyVibratorOnLocked();
+ }
+ return duration;
}
}
@@ -203,7 +207,8 @@
*
* <p>This will affect the state of {@link #isVibrating()}.
*
- * @return The duration of the effect playing, or 0 if unsupported.
+ * @return The positive duration of the vibration started, if successful, zero if the vibrator
+ * do not support the input or a negative number if the operation failed.
*/
public long on(PrebakedSegment prebaked, long vibrationId) {
synchronized (mLock) {
@@ -222,7 +227,8 @@
*
* <p>This will affect the state of {@link #isVibrating()}.
*
- * @return The duration of the effect playing, or 0 if unsupported.
+ * @return The positive duration of the vibration started, if successful, zero if the vibrator
+ * do not support the input or a negative number if the operation failed.
*/
public long on(PrimitiveSegment[] primitives, long vibrationId) {
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
@@ -246,8 +252,17 @@
* @return The duration of the effect playing, or 0 if unsupported.
*/
public long on(RampSegment[] primitives, long vibrationId) {
- // TODO(b/167947076): forward to the HAL once APIs are introduced
- return 0;
+ if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
+ return 0;
+ }
+ synchronized (mLock) {
+ int braking = mVibratorInfo.getDefaultBraking();
+ long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
+ if (duration > 0) {
+ notifyVibratorOnLocked();
+ }
+ return duration;
+ }
}
/** Turns off the vibrator.This will affect the state of {@link #isVibrating()}. */
@@ -327,22 +342,26 @@
*/
private static native long getNativeFinalizer();
private static native boolean isAvailable(long nativePtr);
- private static native void on(long nativePtr, long milliseconds, long vibrationId);
+ private static native long on(long nativePtr, long milliseconds, long vibrationId);
private static native void off(long nativePtr);
private static native void setAmplitude(long nativePtr, float amplitude);
- private static native int[] getSupportedEffects(long nativePtr);
- private static native int[] getSupportedPrimitives(long nativePtr);
private static native long performEffect(long nativePtr, long effect, long strength,
long vibrationId);
+
private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect,
long vibrationId);
+
+ private static native long performPwleEffect(long nativePtr, RampSegment[] effect,
+ int braking, long vibrationId);
+
private static native void setExternalControl(long nativePtr, boolean enabled);
- private static native long getCapabilities(long nativePtr);
+
private static native void alwaysOnEnable(long nativePtr, long id, long effect,
long strength);
+
private static native void alwaysOnDisable(long nativePtr, long id);
- private static native float getResonantFrequency(long nativePtr);
- private static native float getQFactor(long nativePtr);
+
+ private static native VibratorInfo getInfo(long nativePtr, float suggestedFrequencyRange);
private long mNativePtr = 0;
@@ -365,8 +384,8 @@
}
/** Turns vibrator on for given time. */
- public void on(long milliseconds, long vibrationId) {
- on(mNativePtr, milliseconds, vibrationId);
+ public long on(long milliseconds, long vibrationId) {
+ return on(mNativePtr, milliseconds, vibrationId);
}
/** Turns vibrator off. */
@@ -379,36 +398,26 @@
setAmplitude(mNativePtr, amplitude);
}
- /** Returns all predefined effects supported by the device vibrator. */
- public int[] getSupportedEffects() {
- return getSupportedEffects(mNativePtr);
- }
-
- /** Returns all compose primitives supported by the device vibrator. */
- public int[] getSupportedPrimitives() {
- return getSupportedPrimitives(mNativePtr);
- }
-
/** Turns vibrator on to perform one of the supported effects. */
public long perform(long effect, long strength, long vibrationId) {
return performEffect(mNativePtr, effect, strength, vibrationId);
}
- /** Turns vibrator on to perform one of the supported composed effects. */
+ /** Turns vibrator on to perform effect composed of give primitives effect. */
public long compose(PrimitiveSegment[] primitives, long vibrationId) {
return performComposedEffect(mNativePtr, primitives, vibrationId);
}
+ /** Turns vibrator on to perform PWLE effect composed of given primitives. */
+ public long composePwle(RampSegment[] primitives, int braking, long vibrationId) {
+ return performPwleEffect(mNativePtr, primitives, braking, vibrationId);
+ }
+
/** Enabled the device vibrator to be controlled by another service. */
public void setExternalControl(boolean enabled) {
setExternalControl(mNativePtr, enabled);
}
- /** Returns all capabilities of the device vibrator. */
- public long getCapabilities() {
- return getCapabilities(mNativePtr);
- }
-
/** Enable always-on vibration with given id and effect. */
public void alwaysOnEnable(long id, long effect, long strength) {
alwaysOnEnable(mNativePtr, id, effect, strength);
@@ -419,14 +428,9 @@
alwaysOnDisable(mNativePtr, id);
}
- /** Gets the vibrator's resonant frequency (F0) */
- public float getResonantFrequency() {
- return getResonantFrequency(mNativePtr);
- }
-
- /** Gets the vibrator's Q factor */
- public float getQFactor() {
- return getQFactor(mNativePtr);
+ /** Return device vibrator metadata. */
+ public VibratorInfo getInfo(float suggestedFrequencyRange) {
+ return getInfo(mNativePtr, suggestedFrequencyRange);
}
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 5fd1d7a..b1d6050 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -1394,15 +1394,15 @@
while ((nextArg = peekNextArg()) != null) {
switch (nextArg) {
case "-f":
- getNextArgRequired(); // consume the -f argument;
+ getNextArgRequired(); // consume "-f"
force = true;
break;
case "-d":
- getNextArgRequired(); // consume the -d argument;
+ getNextArgRequired(); // consume "-d"
description = getNextArgRequired();
break;
default:
- // Not a common option, finish reading.
+ // nextArg is not a common option, finish reading.
return;
}
}
@@ -1456,14 +1456,9 @@
private int runMono() {
CommonOptions commonOptions = new CommonOptions();
- VibrationEffect effect = nextEffect();
- if (effect == null) {
- return 0;
- }
-
- CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect);
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(nextEffect());
VibrationAttributes attrs = createVibrationAttributes(commonOptions);
- vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combinedEffect, attrs,
+ vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, effect, attrs,
commonOptions.description, mToken);
return 0;
}
@@ -1474,10 +1469,7 @@
CombinedVibrationEffect.startSynced();
while ("-v".equals(getNextOption())) {
int vibratorId = Integer.parseInt(getNextArgRequired());
- VibrationEffect effect = nextEffect();
- if (effect != null) {
- combination.addVibrator(vibratorId, effect);
- }
+ combination.addVibrator(vibratorId, nextEffect());
}
VibrationAttributes attrs = createVibrationAttributes(commonOptions);
vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs,
@@ -1487,19 +1479,11 @@
private int runSequential() {
CommonOptions commonOptions = new CommonOptions();
-
CombinedVibrationEffect.SequentialCombination combination =
CombinedVibrationEffect.startSequential();
while ("-v".equals(getNextOption())) {
int vibratorId = Integer.parseInt(getNextArgRequired());
- int delay = 0;
- if ("-w".equals(getNextOption())) {
- delay = Integer.parseInt(getNextArgRequired());
- }
- VibrationEffect effect = nextEffect();
- if (effect != null) {
- combination.addNext(vibratorId, effect, delay);
- }
+ combination.addNext(vibratorId, nextEffect());
}
VibrationAttributes attrs = createVibrationAttributes(commonOptions);
vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs,
@@ -1512,87 +1496,145 @@
return 0;
}
- @Nullable
private VibrationEffect nextEffect() {
- String effectType = getNextArgRequired();
- if ("oneshot".equals(effectType)) {
- return nextOneShot();
+ VibrationEffect.Composition composition = VibrationEffect.startComposition();
+ String nextArg;
+
+ while ((nextArg = peekNextArg()) != null) {
+ if ("oneshot".equals(nextArg)) {
+ addOneShotToComposition(composition);
+ } else if ("waveform".equals(nextArg)) {
+ addWaveformToComposition(composition);
+ } else if ("prebaked".equals(nextArg)) {
+ addPrebakedToComposition(composition);
+ } else if ("primitives".equals(nextArg)) {
+ addPrimitivesToComposition(composition);
+ } else {
+ // nextArg is not an effect, finish reading.
+ break;
+ }
}
- if ("waveform".equals(effectType)) {
- return nextWaveform();
- }
- if ("prebaked".equals(effectType)) {
- return nextPrebaked();
- }
- if ("composed".equals(effectType)) {
- return nextComposed();
- }
- return null;
+
+ return composition.compose();
}
- private VibrationEffect nextOneShot() {
- boolean hasAmplitude = "-a".equals(getNextOption());
+ private void addOneShotToComposition(VibrationEffect.Composition composition) {
+ boolean hasAmplitude = false;
+ int delay = 0;
+
+ getNextArgRequired(); // consume "oneshot"
+ String nextOption;
+ while ((nextOption = getNextOption()) != null) {
+ if ("-a".equals(nextOption)) {
+ hasAmplitude = true;
+ } else if ("-w".equals(nextOption)) {
+ delay = Integer.parseInt(getNextArgRequired());
+ }
+ }
+
long duration = Long.parseLong(getNextArgRequired());
int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired())
: VibrationEffect.DEFAULT_AMPLITUDE;
- return VibrationEffect.createOneShot(duration, amplitude);
+ composition.addEffect(VibrationEffect.createOneShot(duration, amplitude), delay);
}
- private VibrationEffect nextWaveform() {
+ private void addWaveformToComposition(VibrationEffect.Composition composition) {
boolean hasAmplitudes = false;
+ boolean hasFrequencies = false;
+ boolean isContinuous = false;
int repeat = -1;
+ int delay = 0;
- String nextOption = getNextOption();
- while (nextOption != null) {
+ getNextArgRequired(); // consume "waveform"
+ String nextOption;
+ while ((nextOption = getNextOption()) != null) {
if ("-a".equals(nextOption)) {
hasAmplitudes = true;
} else if ("-r".equals(nextOption)) {
repeat = Integer.parseInt(getNextArgRequired());
+ } else if ("-w".equals(nextOption)) {
+ delay = Integer.parseInt(getNextArgRequired());
+ } else if ("-f".equals(nextOption)) {
+ hasFrequencies = true;
+ } else if ("-c".equals(nextOption)) {
+ isContinuous = true;
}
- nextOption = getNextOption();
}
- List<Long> durations = new ArrayList<>();
- List<Integer> amplitudes = new ArrayList<>();
+ List<Integer> durations = new ArrayList<>();
+ List<Float> amplitudes = new ArrayList<>();
+ List<Float> frequencies = new ArrayList<>();
+ float nextAmplitude = 0;
String nextArg;
- while ((nextArg = peekNextArg()) != null && !"-v".equals(nextArg)) {
- durations.add(Long.parseLong(getNextArgRequired()));
+ while ((nextArg = peekNextArg()) != null) {
+ try {
+ durations.add(Integer.parseInt(nextArg));
+ getNextArgRequired(); // consume the duration
+ } catch (NumberFormatException e) {
+ // nextArg is not a duration, finish reading.
+ break;
+ }
if (hasAmplitudes) {
- amplitudes.add(Integer.parseInt(getNextArgRequired()));
+ amplitudes.add(
+ Float.parseFloat(getNextArgRequired()) / VibrationEffect.MAX_AMPLITUDE);
+ } else {
+ amplitudes.add(nextAmplitude);
+ nextAmplitude = 1 - nextAmplitude;
+ }
+ if (hasFrequencies) {
+ frequencies.add(Float.parseFloat(getNextArgRequired()));
+ } else {
+ frequencies.add(0f);
}
}
- long[] durationArray = durations.stream().mapToLong(Long::longValue).toArray();
- if (!hasAmplitudes) {
- return VibrationEffect.createWaveform(durationArray, repeat);
+ VibrationEffect.WaveformBuilder waveform = VibrationEffect.startWaveform();
+ for (int i = 0; i < durations.size(); i++) {
+ if (isContinuous) {
+ waveform.addRamp(amplitudes.get(i), frequencies.get(i), durations.get(i));
+ } else {
+ waveform.addStep(amplitudes.get(i), frequencies.get(i), durations.get(i));
+ }
+ }
+ composition.addEffect(waveform.build(repeat), delay);
+ }
+
+ private void addPrebakedToComposition(VibrationEffect.Composition composition) {
+ boolean shouldFallback = false;
+ int delay = 0;
+
+ getNextArgRequired(); // consume "prebaked"
+ String nextOption;
+ while ((nextOption = getNextOption()) != null) {
+ if ("-b".equals(nextOption)) {
+ shouldFallback = true;
+ } else if ("-w".equals(nextOption)) {
+ delay = Integer.parseInt(getNextArgRequired());
+ }
}
- int[] amplitudeArray = amplitudes.stream().mapToInt(Integer::intValue).toArray();
- return VibrationEffect.createWaveform(durationArray, amplitudeArray, repeat);
- }
-
- private VibrationEffect nextPrebaked() {
- boolean shouldFallback = "-b".equals(getNextOption());
int effectId = Integer.parseInt(getNextArgRequired());
- return VibrationEffect.get(effectId, shouldFallback);
+ composition.addEffect(VibrationEffect.get(effectId, shouldFallback), delay);
}
- private VibrationEffect nextComposed() {
- VibrationEffect.Composition composition = VibrationEffect.startComposition();
+ private void addPrimitivesToComposition(VibrationEffect.Composition composition) {
+ getNextArgRequired(); // consume "primitives"
String nextArg;
while ((nextArg = peekNextArg()) != null) {
int delay = 0;
if ("-w".equals(nextArg)) {
- getNextArgRequired(); // consume the -w option
+ getNextArgRequired(); // consume "-w"
delay = Integer.parseInt(getNextArgRequired());
- } else if ("-v".equals(nextArg)) {
- // Starting next vibrator, this composed effect if finished.
+ nextArg = peekNextArg();
+ }
+ try {
+ composition.addPrimitive(Integer.parseInt(nextArg), /* scale= */ 1, delay);
+ getNextArgRequired(); // consume the primitive id
+ } catch (NumberFormatException | NullPointerException e) {
+ // nextArg is not describing a primitive, leave it to be consumed by outer loops
break;
}
- int primitiveId = Integer.parseInt(getNextArgRequired());
- composition.addPrimitive(primitiveId, /* scale= */ 1f, delay);
}
- return composition.compose();
}
private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) {
@@ -1615,38 +1657,51 @@
pw.println(" list");
pw.println(" Prints the id of device vibrators. This does not include any ");
pw.println(" connected input device.");
- pw.println(" synced [options] <effect>");
+ pw.println(" synced [options] <effect>...");
pw.println(" Vibrates effect on all vibrators in sync.");
- pw.println(" combined [options] (-v <vibrator-id> <effect>)...");
+ pw.println(" combined [options] (-v <vibrator-id> <effect>...)...");
pw.println(" Vibrates different effects on each vibrator in sync.");
- pw.println(" sequential [options] (-v <vibrator-id> [-w <delay>] <effect>)...");
+ pw.println(" sequential [options] (-v <vibrator-id> <effect>...)...");
pw.println(" Vibrates different effects on each vibrator in sequence.");
pw.println(" cancel");
pw.println(" Cancels any active vibration");
pw.println("");
pw.println("Effect commands:");
- pw.println(" oneshot [-a] <duration> [<amplitude>]");
+ pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]");
pw.println(" Vibrates for duration milliseconds; ignored when device is on ");
pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting ");
pw.println(" will be used to scale amplitude.");
+ pw.println(" If -w is provided, the effect will be played after the specified");
+ pw.println(" wait time in milliseconds.");
pw.println(" If -a is provided, the command accepts a second argument for ");
pw.println(" amplitude, in a scale of 1-255.");
- pw.println(" waveform [-r <index>] [-a] (<duration> [<amplitude>])...");
+ pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] ");
+ pw.println("(<duration> [<amplitude>] [<frequency>])...");
pw.println(" Vibrates for durations and amplitudes in list; ignored when ");
pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength ");
pw.println(" user setting will be used to scale amplitude.");
+ pw.println(" If -w is provided, the effect will be played after the specified");
+ pw.println(" wait time in milliseconds.");
pw.println(" If -r is provided, the waveform loops back to the specified");
pw.println(" index (e.g. 0 loops from the beginning)");
- pw.println(" If -a is provided, the command accepts duration-amplitude pairs;");
- pw.println(" otherwise, it accepts durations only and alternates off/on");
- pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255.");
- pw.println(" prebaked [-b] <effect-id>");
+ pw.println(" If -a is provided, the command expects amplitude to follow each");
+ pw.println(" duration; otherwise, it accepts durations only and alternates");
+ pw.println(" off/on");
+ pw.println(" If -f is provided, the command expects frequency to follow each");
+ pw.println(" amplitude or duration; otherwise, it uses resonant frequency");
+ pw.println(" If -c is provided, the waveform is continuous and will ramp");
+ pw.println(" between values; otherwise each entry is a fixed step.");
+ pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;");
+ pw.println(" frequency is a relative value around resonant frequency 0;");
+ pw.println(" prebaked [-w delay] [-b] <effect-id>");
pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
pw.println(" will be used to scale amplitude.");
+ pw.println(" If -w is provided, the effect will be played after the specified");
+ pw.println(" wait time in milliseconds.");
pw.println(" If -b is provided, the prebaked fallback effect will be played if");
pw.println(" the device doesn't support the given effect-id.");
- pw.println(" composed [-w <delay>] <primitive-id>...");
+ pw.println(" primitives ([-w delay] <primitive-id>)...");
pw.println(" Vibrates with a composed effect; ignored when device is on DND ");
pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
pw.println(" will be used to scale primitive intensities.");
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index b947c88..16692e1 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -514,6 +514,18 @@
}
}
+ void onImeSurfaceShownChanged(WindowState windowState, boolean shown) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".onImeSurfaceShownChanged",
+ "windowState=" + windowState + "; shown=" + shown);
+ }
+ final int displayId = windowState.getDisplayId();
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.onImeSurfaceShownChanged(shown);
+ }
+ }
+
private static void populateTransformationMatrix(WindowState windowState,
Matrix outMatrix) {
windowState.getTransformationMatrix(sTempFloats, outMatrix);
@@ -766,6 +778,15 @@
}
}
+ void onImeSurfaceShownChanged(boolean shown) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".onImeSurfaceShownChanged", "shown=" + shown);
+ }
+ mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED,
+ shown ? 1 : 0, 0).sendToTarget();
+ }
+
MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
if (mAccessibilityTracing.isEnabled()) {
mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow",
@@ -1337,6 +1358,7 @@
public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
+ public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6;
MyHandler(Looper looper) {
super(looper);
@@ -1380,6 +1402,11 @@
}
}
} break;
+
+ case MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED: {
+ final boolean shown = message.arg1 == 1;
+ mCallbacks.onImeWindowVisibilityChanged(shown);
+ } break;
}
}
}
@@ -2123,5 +2150,4 @@
}
}
}
-
}
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 1262dee..5a8af45 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -17,7 +17,9 @@
package com.android.server.wm;
import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH;
+import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
@@ -34,6 +36,9 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -49,15 +54,22 @@
import android.service.displayhash.DisplayHashParams;
import android.service.displayhash.DisplayHasherService;
import android.service.displayhash.IDisplayHasherService;
+import android.util.AttributeSet;
import android.util.Size;
import android.util.Slog;
+import android.util.Xml;
import android.view.MagnificationSpec;
import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -102,6 +114,41 @@
private final Matrix mTmpMatrix = new Matrix();
private final RectF mTmpRectF = new RectF();
+ /**
+ * Lock used when retrieving xml metadata. Lock when retrieving the xml data the first time
+ * since it will be cached after that. Check if {@link #mParsedXml} is set to determine if the
+ * metadata needs to retrieved.
+ */
+ private final Object mParseXmlLock = new Object();
+
+ /**
+ * Flag whether the xml metadata has been retrieved and parsed. Once this is set to true,
+ * there's no need to request metadata again.
+ */
+ @GuardedBy("mParseXmlLock")
+ private boolean mParsedXml;
+
+ /**
+ * Specified throttle time in milliseconds. Don't allow an app to generate a display hash more
+ * than once per throttleTime
+ */
+ private int mThrottleDurationMillis = 0;
+
+ /**
+ * The last time an app requested to generate a display hash in System time.
+ */
+ private long mLastRequestTimeMs;
+
+ /**
+ * The last uid that requested to generate a hash.
+ */
+ private int mLastRequestUid;
+
+ /**
+ * Only used for testing. Throttling should always be enabled unless running tests
+ */
+ private boolean mDisplayHashThrottlingEnabled = true;
+
private interface Command {
void run(IDisplayHasherService service) throws RemoteException;
}
@@ -131,6 +178,10 @@
return results.getParcelable(EXTRA_VERIFIED_DISPLAY_HASH);
}
+ void setDisplayHashThrottlingEnabled(boolean enable) {
+ mDisplayHashThrottlingEnabled = enable;
+ }
+
private void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
String hashAlgorithm, RemoteCallback callback) {
connectAndRun(
@@ -138,8 +189,36 @@
callback));
}
+ private boolean allowedToGenerateHash(int uid) {
+ if (!mDisplayHashThrottlingEnabled) {
+ // Always allow to generate the hash. This is used to allow tests to run without
+ // waiting on the designated threshold.
+ return true;
+ }
+
+ long currentTime = System.currentTimeMillis();
+ if (mLastRequestUid != uid) {
+ mLastRequestUid = uid;
+ mLastRequestTimeMs = currentTime;
+ return true;
+ }
+
+ int throttleDurationMs = getThrottleDurationMillis();
+ if (currentTime - mLastRequestTimeMs < throttleDurationMs) {
+ return false;
+ }
+
+ mLastRequestTimeMs = currentTime;
+ return true;
+ }
+
void generateDisplayHash(SurfaceControl.LayerCaptureArgs.Builder args,
- Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback) {
+ Rect boundsInWindow, String hashAlgorithm, int uid, RemoteCallback callback) {
+ if (!allowedToGenerateHash(uid)) {
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS);
+ return;
+ }
+
final Map<String, DisplayHashParams> displayHashAlgorithmsMap = getDisplayHashAlgorithms();
DisplayHashParams displayHashParams = displayHashAlgorithmsMap.get(hashAlgorithm);
if (displayHashParams == null) {
@@ -277,6 +356,64 @@
}
}
+ private int getThrottleDurationMillis() {
+ if (!parseXmlProperties()) {
+ return 0;
+ }
+ return mThrottleDurationMillis;
+ }
+
+ private boolean parseXmlProperties() {
+ // We have a separate lock for the xml parsing since it doesn't need to make the
+ // request through the service connection. Instead, we have a lock to ensure we can
+ // properly cache the xml metadata so we don't need to call into the ExtServices
+ // process for each request.
+ synchronized (mParseXmlLock) {
+ if (mParsedXml) {
+ return true;
+ }
+
+ final ServiceInfo serviceInfo = getServiceInfo();
+ if (serviceInfo == null) return false;
+
+ final PackageManager pm = mContext.getPackageManager();
+
+ XmlResourceParser parser;
+ parser = serviceInfo.loadXmlMetaData(pm, SERVICE_META_DATA);
+ if (parser == null) {
+ return false;
+ }
+
+ Resources res;
+ try {
+ res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while (true) {
+ try {
+ if (!((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG)) {
+ break;
+ }
+ } catch (XmlPullParserException | IOException e) {
+ return false;
+ }
+ }
+
+ TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHasherService);
+ mThrottleDurationMillis = sa.getInt(
+ R.styleable.DisplayHasherService_throttleDurationMillis, 0);
+ sa.recycle();
+ mParsedXml = true;
+ return true;
+ }
+ }
+
/**
* Run a command, starting the service connection if necessary.
*/
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6046cc6..9ff701c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -1539,20 +1539,6 @@
}
}
- @Override
- public boolean canUseRotationResolver() {
- if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
-
- switch (mCurrentAppOrientation) {
- case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
- case ActivityInfo.SCREEN_ORIENTATION_USER:
- case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
- case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
- case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
- return true;
- }
- return false;
- }
@Override
public void onProposedRotationChanged(int rotation) {
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 38ad4f0..7b0fb01 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -30,7 +30,6 @@
import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET;
import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL;
import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET;
-import static com.android.server.wm.InsetsSourceProviderProto.FINISH_SEAMLESS_ROTATE_FRAME_NUMBER;
import static com.android.server.wm.InsetsSourceProviderProto.FRAME;
import static com.android.server.wm.InsetsSourceProviderProto.IME_OVERRIDDEN_FRAME;
import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING;
@@ -60,6 +59,7 @@
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* Controller for a specific inset source on the server. It's called provider as it provides the
@@ -85,6 +85,16 @@
private final Rect mImeOverrideFrame = new Rect();
private boolean mIsLeashReadyForDispatching;
+ private final Consumer<Transaction> mSetLeashPositionConsumer = t -> {
+ if (mControl != null) {
+ final SurfaceControl leash = mControl.getLeash();
+ if (leash != null) {
+ final Point position = mControl.getSurfacePosition();
+ t.setPosition(leash, position.x, position.y);
+ }
+ }
+ };
+
/** The visibility override from the current controlling window. */
private boolean mClientVisible;
@@ -151,7 +161,6 @@
// TODO: Ideally, we should wait for the animation to finish so previous window can
// animate-out as new one animates-in.
mWin.cancelAnimation();
- mWin.mPendingPositionChanged = null;
mWin.mProvidedInsetsSources.remove(mSource.getType());
}
ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win);
@@ -252,12 +261,11 @@
final Point position = getWindowFrameSurfacePosition();
if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) {
changed = true;
- if (!mWin.getWindowFrames().didFrameSizeChange()) {
- updateLeashPosition(-1 /* frameNumber */);
- } else if (mWin.mInRelayout) {
- updateLeashPosition(mWin.getFrameNumber());
+ if (mWin.getWindowFrames().didFrameSizeChange() && mWin.mWinAnimator.getShown()
+ && mWin.okToDisplay()) {
+ mWin.applyWithNextDraw(mSetLeashPositionConsumer);
} else {
- mWin.mPendingPositionChanged = this;
+ mSetLeashPositionConsumer.accept(mWin.getPendingTransaction());
}
}
final Insets insetsHint = mSource.calculateInsets(
@@ -272,19 +280,6 @@
}
}
- void updateLeashPosition(long frameNumber) {
- if (mControl == null) {
- return;
- }
- final SurfaceControl leash = mControl.getLeash();
- if (leash != null) {
- final Transaction t = mDisplayContent.getPendingTransaction();
- final Point position = mControl.getSurfacePosition();
- t.setPosition(leash, position.x, position.y);
- deferTransactionUntil(t, leash, frameNumber);
- }
- }
-
private Point getWindowFrameSurfacePosition() {
final Rect frame = mWin.getFrame();
final Point position = new Point();
@@ -292,14 +287,6 @@
return position;
}
- private void deferTransactionUntil(Transaction t, SurfaceControl leash, long frameNumber) {
- if (frameNumber >= 0) {
- final SurfaceControl barrier = mWin.getClientViewRootSurface();
- t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber);
- t.deferTransactionUntil(leash, barrier, frameNumber);
- }
- }
-
/**
* @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget)
*/
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7c5afa8..7644cf2 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -43,7 +43,6 @@
import android.util.ArraySet;
import android.util.MathUtils;
import android.util.Slog;
-import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -296,9 +295,9 @@
}
boolean updateWallpaperOffset(WindowState wallpaperWin, boolean sync) {
- final DisplayInfo displayInfo = wallpaperWin.getDisplayInfo();
- final int dw = displayInfo.logicalWidth;
- final int dh = displayInfo.logicalHeight;
+ final Rect parentFrame = wallpaperWin.getParentFrame();
+ final int dw = parentFrame.width();
+ final int dh = parentFrame.height();
int xOffset = 0;
int yOffset = 0;
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 9245f8c..ffd6d21 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -113,7 +113,7 @@
}
/**
- * @return true if the width or height has changed since last reported to the client.
+ * @return true if the width or height has changed since last updating resizing window.
*/
boolean didFrameSizeChange() {
return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
@@ -135,6 +135,13 @@
}
/**
+ * @return true if the width or height has changed since last reported to the client.
+ */
+ boolean isFrameSizeChangeReported() {
+ return mFrameSizeChanged || didFrameSizeChange();
+ }
+
+ /**
* Resets the size changed flags so they're all set to false again. This should be called
* after the frames are reported to client.
*/
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 8148f15..ab515d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -113,7 +113,7 @@
*
* @param magnificationRegion the current magnification region
*/
- public void onMagnificationRegionChanged(Region magnificationRegion);
+ void onMagnificationRegionChanged(Region magnificationRegion);
/**
* Called when an application requests a rectangle on the screen to allow
@@ -124,20 +124,27 @@
* @param right The rectangle right.
* @param bottom The rectangle bottom.
*/
- public void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
+ void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
/**
* Notifies that the rotation changed.
*
* @param rotation The current rotation.
*/
- public void onRotationChanged(int rotation);
+ void onRotationChanged(int rotation);
/**
* Notifies that the context of the user changed. For example, an application
* was started.
*/
- public void onUserContextChanged();
+ void onUserContextChanged();
+
+ /**
+ * Notifies that the IME window visibility changed.
+ * @param shown {@code true} means the IME window shows on the screen. Otherwise it's
+ * hidden.
+ */
+ void onImeWindowVisibilityChanged(boolean shown);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a8ca5b6..a670b39 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2228,13 +2228,6 @@
win.setFrameNumber(frameNumber);
- final DisplayContent dc = win.getDisplayContent();
-
- if (win.mPendingPositionChanged != null) {
- win.mPendingPositionChanged.updateLeashPosition(frameNumber);
- win.mPendingPositionChanged = null;
- }
-
int attrChanges = 0;
int flagChanges = 0;
int privateFlagChanges = 0;
@@ -8633,6 +8626,14 @@
return mDisplayHashController.verifyDisplayHash(displayHash);
}
+ @Override
+ public void setDisplayHashThrottlingEnabled(boolean enable) {
+ if (!checkCallingPermission(READ_FRAME_BUFFER, "setDisplayHashThrottle()")) {
+ throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+ }
+ mDisplayHashController.setDisplayHashThrottlingEnabled(enable);
+ }
+
void generateDisplayHash(Session session, IWindow window, Rect boundsInWindow,
String hashAlgorithm, RemoteCallback callback) {
final SurfaceControl displaySurfaceControl;
@@ -8646,6 +8647,13 @@
return;
}
+ if (win.mActivityRecord == null || !win.mActivityRecord.isState(
+ Task.ActivityState.RESUMED)) {
+ mDisplayHashController.sendDisplayHashError(callback,
+ DISPLAY_HASH_ERROR_MISSING_WINDOW);
+ return;
+ }
+
DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display");
@@ -8676,8 +8684,8 @@
.setUid(uid)
.setSourceCrop(boundsInDisplay);
- mDisplayHashController.generateDisplayHash(args, boundsInWindow,
- hashAlgorithm, callback);
+ mDisplayHashController.generateDisplayHash(args, boundsInWindow, hashAlgorithm, uid,
+ callback);
}
boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index 49e704f..b971cab 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -275,19 +275,6 @@
}
}
- /**
- * Returns true if the current status of the phone is suitable for using rotation resolver
- * service.
- *
- * To reduce the power consumption of rotation resolver service, rotation query should run less
- * frequently than other low power orientation sensors. This method is used to check whether
- * the current status of the phone is necessary to request a suggested screen rotation from the
- * rotation resolver service. Note that it always returns {@code false} in the base class. It
- * should be overridden in the derived classes.
- */
- public boolean canUseRotationResolver() {
- return false;
- }
/**
* Returns true if the rotation resolver feature is enabled by setting. It means {@link
@@ -1155,7 +1142,7 @@
}
}
- if (isRotationResolverEnabled() && canUseRotationResolver()) {
+ if (isRotationResolverEnabled()) {
if (mRotationResolverService == null) {
mRotationResolverService = LocalServices.getService(
RotationResolverInternal.class);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 48d4fc5..92c9522 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -726,8 +726,6 @@
*/
private InsetsState mFrozenInsetsState;
- @Nullable InsetsSourceProvider mPendingPositionChanged;
-
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
private KeyInterceptionInfo mKeyInterceptionInfo;
@@ -824,6 +822,12 @@
updateSurfacePosition(t);
};
+ private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
+ if (mSurfaceControl != null && mSurfaceControl.isValid()) {
+ t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+ }
+ };
+
/**
* @see #setSurfaceTranslationY(int)
*/
@@ -2180,18 +2184,7 @@
final int left = mWindowFrames.mFrame.left;
final int top = mWindowFrames.mFrame.top;
- // During the transition from pip to fullscreen, the activity windowing mode is set to
- // fullscreen at the beginning while the task is kept in pinned mode. Skip the move
- // animation in such case since the transition is handled in SysUI.
- final boolean hasMovementAnimation = getTask() == null
- ? getWindowConfiguration().hasMovementAnimations()
- : getTask().getWindowConfiguration().hasMovementAnimations();
- if (mToken.okToAnimate()
- && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
- && !isDragResizing()
- && hasMovementAnimation
- && !mWinAnimator.mLastHidden
- && !mSeamlesslyRotated) {
+ if (canPlayMoveAnimation()) {
startMoveAnimation(left, top);
}
@@ -2207,6 +2200,22 @@
mMovedByResize = false;
}
+ private boolean canPlayMoveAnimation() {
+
+ // During the transition from pip to fullscreen, the activity windowing mode is set to
+ // fullscreen at the beginning while the task is kept in pinned mode. Skip the move
+ // animation in such case since the transition is handled in SysUI.
+ final boolean hasMovementAnimation = getTask() == null
+ ? getWindowConfiguration().hasMovementAnimations()
+ : getTask().getWindowConfiguration().hasMovementAnimations();
+ return mToken.okToAnimate()
+ && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
+ && !isDragResizing()
+ && hasMovementAnimation
+ && !mWinAnimator.mLastHidden
+ && !mSeamlesslyRotated;
+ }
+
/**
* Return whether this window has moved. (Only makes
* sense to call from performLayoutAndPlaceSurfacesLockedInner().)
@@ -3453,6 +3462,9 @@
if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST) {
mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown);
}
+ if (mIsImWindow && mWmService.mAccessibilityController != null) {
+ mWmService.mAccessibilityController.onImeSurfaceShownChanged(this, shown);
+ }
}
private void logExclusionRestrictions(int side) {
@@ -5377,13 +5389,18 @@
// prior to the rotation.
if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null
&& !mLastSurfacePosition.equals(mSurfacePosition)) {
- t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+ final boolean frameSizeChanged = mWindowFrames.isFrameSizeChangeReported();
+ final boolean surfaceInsetsChanged = surfaceInsetsChanging();
+ final boolean surfaceSizeChanged = frameSizeChanged || surfaceInsetsChanged;
mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
- if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) {
+ if (surfaceInsetsChanged) {
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
- t.deferTransactionUntil(mSurfaceControl,
- mWinAnimator.mSurfaceController.mSurfaceControl,
- getFrameNumber());
+ }
+ if (surfaceSizeChanged && mWinAnimator.getShown() && !canPlayMoveAnimation()
+ && okToDisplay()) {
+ applyWithNextDraw(mSetSurfacePositionConsumer);
+ } else {
+ mSetSurfacePositionConsumer.accept(t);
}
}
}
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 7f8168a..cf64a68 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -39,11 +39,22 @@
static JavaVM* sJvm = nullptr;
static jmethodID sMethodIdOnComplete;
+static jclass sFrequencyMappingClass;
+static jmethodID sFrequencyMappingCtor;
+static jclass sVibratorInfoClass;
+static jmethodID sVibratorInfoCtor;
static struct {
jfieldID id;
jfieldID scale;
jfieldID delay;
} sPrimitiveClassInfo;
+static struct {
+ jfieldID startAmplitude;
+ jfieldID endAmplitude;
+ jfieldID startFrequency;
+ jfieldID endFrequency;
+ jfieldID duration;
+} sRampClassInfo;
static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
@@ -97,7 +108,17 @@
jniEnv->DeleteGlobalRef(mCallbackListener);
}
- vibrator::HalController* hal() const { return mHal.get(); }
+ int32_t getVibratorId() const { return mVibratorId; }
+
+ vibrator::Info getVibratorInfo() { return mHal->getInfo(); }
+
+ void initHal() { mHal->init(); }
+
+ template <typename T>
+ vibrator::HalResult<T> halCall(const vibrator::HalFunction<vibrator::HalResult<T>>& fn,
+ const char* functionName) {
+ return mHal->doWithRetry(fn, functionName);
+ }
std::function<void()> createCallback(jlong vibrationId) {
return [vibrationId, this]() {
@@ -113,6 +134,37 @@
const jobject mCallbackListener;
};
+static aidl::BrakingPwle brakingPwle(aidl::Braking braking, int32_t duration) {
+ aidl::BrakingPwle pwle;
+ pwle.braking = braking;
+ pwle.duration = duration;
+ return pwle;
+}
+
+static aidl::ActivePwle activePwleFromJavaPrimitive(JNIEnv* env, jobject ramp) {
+ aidl::ActivePwle pwle;
+ pwle.startAmplitude =
+ static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startAmplitude));
+ pwle.endAmplitude = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endAmplitude));
+ pwle.startFrequency =
+ static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startFrequency));
+ pwle.endFrequency = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endFrequency));
+ pwle.duration = static_cast<int32_t>(env->GetIntField(ramp, sRampClassInfo.duration));
+ return pwle;
+}
+
+/* Return true if braking is not NONE and the active PWLE starts and ends with zero amplitude. */
+static bool shouldBeReplacedWithBraking(aidl::ActivePwle activePwle, aidl::Braking braking) {
+ return (braking != aidl::Braking::NONE) && (activePwle.startAmplitude == 0) &&
+ (activePwle.endAmplitude == 0);
+}
+
+/* Return true if braking is not NONE and the active PWLE only ends with zero amplitude. */
+static bool shouldAddLastBraking(aidl::ActivePwle lastActivePwle, aidl::Braking braking) {
+ return (braking != aidl::Braking::NONE) && (lastActivePwle.startAmplitude > 0) &&
+ (lastActivePwle.endAmplitude == 0);
+}
+
static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) {
aidl::CompositeEffect effect;
effect.primitive = static_cast<aidl::CompositePrimitive>(
@@ -133,7 +185,7 @@
jobject callbackListener) {
std::unique_ptr<VibratorControllerWrapper> wrapper =
std::make_unique<VibratorControllerWrapper>(env, vibratorId, callbackListener);
- wrapper->hal()->init();
+ wrapper->initHal();
return reinterpret_cast<jlong>(wrapper.release());
}
@@ -147,18 +199,23 @@
ALOGE("vibratorIsAvailable failed because native wrapper was not initialized");
return JNI_FALSE;
}
- return wrapper->hal()->ping().isOk() ? JNI_TRUE : JNI_FALSE;
+ auto pingFn = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->ping(); };
+ return wrapper->halCall<void>(pingFn, "ping").isOk() ? JNI_TRUE : JNI_FALSE;
}
-static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs,
- jlong vibrationId) {
+static jlong vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs,
+ jlong vibrationId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorOn failed because native wrapper was not initialized");
- return;
+ return -1;
}
auto callback = wrapper->createCallback(vibrationId);
- wrapper->hal()->on(std::chrono::milliseconds(timeoutMs), callback);
+ auto onFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) {
+ return hal->on(std::chrono::milliseconds(timeoutMs), callback);
+ };
+ auto result = wrapper->halCall<void>(onFn, "on");
+ return result.isOk() ? timeoutMs : (result.isUnsupported() ? 0 : -1);
}
static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong ptr) {
@@ -167,7 +224,8 @@
ALOGE("vibratorOff failed because native wrapper was not initialized");
return;
}
- wrapper->hal()->off();
+ auto offFn = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); };
+ wrapper->halCall<void>(offFn, "off");
}
static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong ptr, jfloat amplitude) {
@@ -176,7 +234,10 @@
ALOGE("vibratorSetAmplitude failed because native wrapper was not initialized");
return;
}
- wrapper->hal()->setAmplitude(static_cast<float>(amplitude));
+ auto setAmplitudeFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) {
+ return hal->setAmplitude(static_cast<float>(amplitude));
+ };
+ wrapper->halCall<void>(setAmplitudeFn, "setAmplitude");
}
static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong ptr,
@@ -186,41 +247,10 @@
ALOGE("vibratorSetExternalControl failed because native wrapper was not initialized");
return;
}
- wrapper->hal()->setExternalControl(enabled);
-}
-
-static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong ptr) {
- VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
- if (wrapper == nullptr) {
- ALOGE("vibratorGetSupportedEffects failed because native wrapper was not initialized");
- return nullptr;
- }
- auto result = wrapper->hal()->getSupportedEffects();
- if (!result.isOk()) {
- return nullptr;
- }
- std::vector<aidl::Effect> supportedEffects = result.value();
- jintArray effects = env->NewIntArray(supportedEffects.size());
- env->SetIntArrayRegion(effects, 0, supportedEffects.size(),
- reinterpret_cast<jint*>(supportedEffects.data()));
- return effects;
-}
-
-static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, jlong ptr) {
- VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
- if (wrapper == nullptr) {
- ALOGE("vibratorGetSupportedPrimitives failed because native wrapper was not initialized");
- return nullptr;
- }
- auto result = wrapper->hal()->getSupportedPrimitives();
- if (!result.isOk()) {
- return nullptr;
- }
- std::vector<aidl::CompositePrimitive> supportedPrimitives = result.value();
- jintArray primitives = env->NewIntArray(supportedPrimitives.size());
- env->SetIntArrayRegion(primitives, 0, supportedPrimitives.size(),
- reinterpret_cast<jint*>(supportedPrimitives.data()));
- return primitives;
+ auto setExternalControlFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) {
+ return hal->setExternalControl(enabled);
+ };
+ wrapper->halCall<void>(setExternalControlFn, "setExternalControl");
}
static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong effect,
@@ -233,8 +263,11 @@
aidl::Effect effectType = static_cast<aidl::Effect>(effect);
aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength);
auto callback = wrapper->createCallback(vibrationId);
- auto result = wrapper->hal()->performEffect(effectType, effectStrength, callback);
- return result.isOk() ? result.value().count() : -1;
+ auto performEffectFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) {
+ return hal->performEffect(effectType, effectStrength, callback);
+ };
+ auto result = wrapper->halCall<std::chrono::milliseconds>(performEffectFn, "performEffect");
+ return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1);
}
static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
@@ -251,18 +284,46 @@
effects.push_back(effectFromJavaPrimitive(env, element));
}
auto callback = wrapper->createCallback(vibrationId);
- auto result = wrapper->hal()->performComposedEffect(effects, callback);
- return result.isOk() ? result.value().count() : -1;
+ auto performComposedEffectFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) {
+ return hal->performComposedEffect(effects, callback);
+ };
+ auto result = wrapper->halCall<std::chrono::milliseconds>(performComposedEffectFn,
+ "performComposedEffect");
+ return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1);
}
-static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+static jlong vibratorPerformPwleEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jobjectArray waveform, jint brakingId, jlong vibrationId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
- ALOGE("vibratorGetCapabilities failed because native wrapper was not initialized");
- return 0;
+ ALOGE("vibratorPerformPwleEffect failed because native wrapper was not initialized");
+ return -1;
}
- auto result = wrapper->hal()->getCapabilities();
- return result.isOk() ? static_cast<jlong>(result.value()) : 0;
+ aidl::Braking braking = static_cast<aidl::Braking>(brakingId);
+ size_t size = env->GetArrayLength(waveform);
+ std::vector<aidl::PrimitivePwle> primitives;
+ std::chrono::milliseconds totalDuration(0);
+ for (size_t i = 0; i < size; i++) {
+ jobject element = env->GetObjectArrayElement(waveform, i);
+ aidl::ActivePwle activePwle = activePwleFromJavaPrimitive(env, element);
+ if ((i > 0) && shouldBeReplacedWithBraking(activePwle, braking)) {
+ primitives.push_back(brakingPwle(braking, activePwle.duration));
+ } else {
+ primitives.push_back(activePwle);
+ }
+ totalDuration += std::chrono::milliseconds(activePwle.duration);
+
+ if ((i == (size - 1)) && shouldAddLastBraking(activePwle, braking)) {
+ primitives.push_back(brakingPwle(braking, 0 /* duration */));
+ }
+ }
+
+ auto callback = wrapper->createCallback(vibrationId);
+ auto performPwleEffectFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) {
+ return hal->performPwleEffect(primitives, callback);
+ };
+ auto result = wrapper->halCall<void>(performPwleEffectFn, "performPwleEffect");
+ return result.isOk() ? totalDuration.count() : (result.isUnsupported() ? 0 : -1);
}
static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong id,
@@ -272,8 +333,11 @@
ALOGE("vibratorAlwaysOnEnable failed because native wrapper was not initialized");
return;
}
- wrapper->hal()->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect),
+ auto alwaysOnEnableFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) {
+ return hal->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect),
static_cast<aidl::EffectStrength>(strength));
+ };
+ wrapper->halCall<void>(alwaysOnEnableFn, "alwaysOnEnable");
}
static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong id) {
@@ -282,27 +346,64 @@
ALOGE("vibratorAlwaysOnDisable failed because native wrapper was not initialized");
return;
}
- wrapper->hal()->alwaysOnDisable(static_cast<int32_t>(id));
+ auto alwaysOnDisableFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) {
+ return hal->alwaysOnDisable(static_cast<int32_t>(id));
+ };
+ wrapper->halCall<void>(alwaysOnDisableFn, "alwaysOnDisable");
}
-static float vibratorGetResonantFrequency(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+static jobject vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jfloat suggestedSafeRange) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
- ALOGE("vibratorGetResonantFrequency failed because native wrapper was not initialized");
- return NAN;
+ ALOGE("vibratorGetInfo failed because native wrapper was not initialized");
+ return nullptr;
}
- auto result = wrapper->hal()->getResonantFrequency();
- return result.isOk() ? static_cast<jfloat>(result.value()) : NAN;
-}
+ vibrator::Info info = wrapper->getVibratorInfo();
-static float vibratorGetQFactor(JNIEnv* env, jclass /* clazz */, jlong ptr) {
- VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
- if (wrapper == nullptr) {
- ALOGE("vibratorGetQFactor failed because native wrapper was not initialized");
- return NAN;
+ jlong capabilities =
+ static_cast<jlong>(info.capabilities.valueOr(vibrator::Capabilities::NONE));
+ jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
+ jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN));
+ jfloat frequencyResolution = static_cast<jfloat>(info.frequencyResolution.valueOr(NAN));
+ jfloat qFactor = static_cast<jfloat>(info.qFactor.valueOr(NAN));
+ jintArray supportedEffects = nullptr;
+ jintArray supportedBraking = nullptr;
+ jintArray supportedPrimitives = nullptr;
+ jfloatArray maxAmplitudes = nullptr;
+
+ if (info.supportedEffects.isOk()) {
+ std::vector<aidl::Effect> effects = info.supportedEffects.value();
+ supportedEffects = env->NewIntArray(effects.size());
+ env->SetIntArrayRegion(supportedEffects, 0, effects.size(),
+ reinterpret_cast<jint*>(effects.data()));
}
- auto result = wrapper->hal()->getQFactor();
- return result.isOk() ? static_cast<jfloat>(result.value()) : NAN;
+ if (info.supportedBraking.isOk()) {
+ std::vector<aidl::Braking> braking = info.supportedBraking.value();
+ supportedBraking = env->NewIntArray(braking.size());
+ env->SetIntArrayRegion(supportedBraking, 0, braking.size(),
+ reinterpret_cast<jint*>(braking.data()));
+ }
+ if (info.supportedPrimitives.isOk()) {
+ std::vector<aidl::CompositePrimitive> primitives = info.supportedPrimitives.value();
+ supportedPrimitives = env->NewIntArray(primitives.size());
+ env->SetIntArrayRegion(supportedPrimitives, 0, primitives.size(),
+ reinterpret_cast<jint*>(primitives.data()));
+ }
+ if (info.maxAmplitudes.isOk()) {
+ std::vector<float> amplitudes = info.maxAmplitudes.value();
+ maxAmplitudes = env->NewFloatArray(amplitudes.size());
+ env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
+ reinterpret_cast<jfloat*>(amplitudes.data()));
+ }
+
+ jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor,
+ minFrequency, resonantFrequency, frequencyResolution,
+ suggestedSafeRange, maxAmplitudes);
+
+ return env->NewObject(sVibratorInfoClass, sVibratorInfoCtor, wrapper->getVibratorId(),
+ capabilities, supportedEffects, supportedBraking, supportedPrimitives,
+ qFactor, frequencyMapping);
}
static const JNINativeMethod method_table[] = {
@@ -311,20 +412,18 @@
(void*)vibratorNativeInit},
{"getNativeFinalizer", "()J", (void*)vibratorGetNativeFinalizer},
{"isAvailable", "(J)Z", (void*)vibratorIsAvailable},
- {"on", "(JJJ)V", (void*)vibratorOn},
+ {"on", "(JJJ)J", (void*)vibratorOn},
{"off", "(J)V", (void*)vibratorOff},
{"setAmplitude", "(JF)V", (void*)vibratorSetAmplitude},
{"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
{"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;J)J",
(void*)vibratorPerformComposedEffect},
- {"getSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
- {"getSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
+ {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJ)J",
+ (void*)vibratorPerformPwleEffect},
{"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
- {"getCapabilities", "(J)J", (void*)vibratorGetCapabilities},
{"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
{"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
- {"getResonantFrequency", "(J)F", (void*)vibratorGetResonantFrequency},
- {"getQFactor", "(J)F", (void*)vibratorGetQFactor},
+ {"getInfo", "(JF)Landroid/os/VibratorInfo;", (void*)vibratorGetInfo},
};
int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) {
@@ -339,6 +438,22 @@
sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "mScale", "F");
sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "mDelay", "I");
+ jclass rampClass = FindClassOrDie(env, "android/os/vibrator/RampSegment");
+ sRampClassInfo.startAmplitude = GetFieldIDOrDie(env, rampClass, "mStartAmplitude", "F");
+ sRampClassInfo.endAmplitude = GetFieldIDOrDie(env, rampClass, "mEndAmplitude", "F");
+ sRampClassInfo.startFrequency = GetFieldIDOrDie(env, rampClass, "mStartFrequency", "F");
+ sRampClassInfo.endFrequency = GetFieldIDOrDie(env, rampClass, "mEndFrequency", "F");
+ sRampClassInfo.duration = GetFieldIDOrDie(env, rampClass, "mDuration", "I");
+
+ jclass frequencyMappingClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyMapping");
+ sFrequencyMappingClass = (jclass)env->NewGlobalRef(frequencyMappingClass);
+ sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V");
+
+ jclass vibratorInfoClass = FindClassOrDie(env, "android/os/VibratorInfo");
+ sVibratorInfoClass = (jclass)env->NewGlobalRef(vibratorInfoClass);
+ sVibratorInfoCtor = GetMethodIDOrDie(env, sVibratorInfoClass, "<init>",
+ "(IJ[I[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V");
+
return jniRegisterNativeMethods(env,
"com/android/server/vibrator/VibratorController$NativeWrapper",
method_table, NELEM(method_table));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 56e2385..a419bf8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_DISABLED;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
@@ -69,6 +70,10 @@
"disable-bt-contacts-sharing";
private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
+ private static final String TAG_NEARBY_NOTIFICATION_STREAMING_POLICY =
+ "nearby-notification-streaming-policy";
+ private static final String TAG_NEARBY_APP_STREAMING_POLICY =
+ "nearby-app-streaming-policy";
private static final String TAG_REQUIRE_AUTO_TIME = "require_auto_time";
private static final String TAG_FORCE_EPHEMERAL_USERS = "force_ephemeral_users";
private static final String TAG_IS_NETWORK_LOGGING_ENABLED = "is_network_logging_enabled";
@@ -159,6 +164,12 @@
@DevicePolicyManager.PasswordComplexity
int mPasswordComplexity = PASSWORD_COMPLEXITY_NONE;
+ @DevicePolicyManager.NearbyStreamingPolicy
+ int mNearbyNotificationStreamingPolicy = NEARBY_STREAMING_DISABLED;
+
+ @DevicePolicyManager.NearbyStreamingPolicy
+ int mNearbyAppStreamingPolicy = NEARBY_STREAMING_DISABLED;
+
@Nullable
FactoryResetProtectionPolicy mFactoryResetProtectionPolicy = null;
@@ -549,6 +560,14 @@
if (mPasswordComplexity != PASSWORD_COMPLEXITY_NONE) {
writeAttributeValueToXml(out, TAG_PASSWORD_COMPLEXITY, mPasswordComplexity);
}
+ if (mNearbyNotificationStreamingPolicy != NEARBY_STREAMING_DISABLED) {
+ writeAttributeValueToXml(out, TAG_NEARBY_NOTIFICATION_STREAMING_POLICY,
+ mNearbyNotificationStreamingPolicy);
+ }
+ if (mNearbyAppStreamingPolicy != NEARBY_STREAMING_DISABLED) {
+ writeAttributeValueToXml(out, TAG_NEARBY_APP_STREAMING_POLICY,
+ mNearbyAppStreamingPolicy);
+ }
if (!TextUtils.isEmpty(mOrganizationId)) {
writeTextToXml(out, TAG_ORGANIZATION_ID, mOrganizationId);
}
@@ -794,6 +813,10 @@
mCommonCriteriaMode = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_PASSWORD_COMPLEXITY.equals(tag)) {
mPasswordComplexity = parser.getAttributeInt(null, ATTR_VALUE);
+ } else if (TAG_NEARBY_NOTIFICATION_STREAMING_POLICY.equals(tag)) {
+ mNearbyNotificationStreamingPolicy = parser.getAttributeInt(null, ATTR_VALUE);
+ } else if (TAG_NEARBY_APP_STREAMING_POLICY.equals(tag)) {
+ mNearbyAppStreamingPolicy = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_ORGANIZATION_ID.equals(tag)) {
type = parser.next();
if (type == TypedXmlPullParser.TEXT) {
@@ -1154,6 +1177,12 @@
pw.print("mPasswordComplexity=");
pw.println(mPasswordComplexity);
+ pw.print("mNearbyNotificationStreamingPolicy=");
+ pw.println(mNearbyNotificationStreamingPolicy);
+
+ pw.print("mNearbyAppStreamingPolicy=");
+ pw.println(mNearbyAppStreamingPolicy);
+
if (!TextUtils.isEmpty(mOrganizationId)) {
pw.print("mOrganizationId=");
pw.println(mOrganizationId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index cdd5a92b..55ab8c3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -109,6 +109,14 @@
return 0;
}
+ @Override
+ public void acknowledgeDeviceCompliant() {}
+
+ @Override
+ public boolean isComplianceAcknowledgementRequired() {
+ return false;
+ }
+
public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
return false;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 44791b0..1858712 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -22,6 +22,7 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
@@ -62,6 +63,7 @@
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_DISABLED;
import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
@@ -687,12 +689,19 @@
private static final boolean ENABLE_LOCK_GUARD = true;
- /** Profile off deadline is not set or more than MANAGED_PROFILE_OFF_WARNING_PERIOD away. */
- private static final int PROFILE_OFF_DEADLINE_DEFAULT = 0;
- /** Profile off deadline is closer than MANAGED_PROFILE_OFF_WARNING_PERIOD. */
- private static final int PROFILE_OFF_DEADLINE_WARNING = 1;
- /** Profile off deadline reached, notify the user that personal apps blocked. */
- private static final int PROFILE_OFF_DEADLINE_REACHED = 2;
+ /**
+ * Profile off deadline is not set or more than MANAGED_PROFILE_OFF_WARNING_PERIOD away, or the
+ * user is running unlocked, no need for notification.
+ */
+ private static final int PROFILE_OFF_NOTIFICATION_NONE = 0;
+ /**
+ * Profile off deadline is closer than MANAGED_PROFILE_OFF_WARNING_PERIOD.
+ */
+ private static final int PROFILE_OFF_NOTIFICATION_WARNING = 1;
+ /**
+ * Profile off deadline reached, notify the user that personal apps blocked.
+ */
+ private static final int PROFILE_OFF_NOTIFICATION_SUSPENDED = 2;
interface Stats {
int LOCK_GUARD_GUARD = 0;
@@ -889,10 +898,9 @@
}
if (isManagedProfile(userHandle)) {
Slog.d(LOG_TAG, "Managed profile became unlocked");
- if (updatePersonalAppsSuspension(userHandle, true /* unlocked */)
- == PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT) {
- triggerPolicyComplianceCheck(userHandle);
- }
+ final boolean suspended =
+ updatePersonalAppsSuspension(userHandle, true /* unlocked */);
+ triggerPolicyComplianceCheckIfNeeded(userHandle, suspended);
}
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
handlePackagesChanged(null /* check all admins */, userHandle);
@@ -7465,6 +7473,78 @@
});
}
+ @Override
+ public void setNearbyNotificationStreamingPolicy(int policy) {
+ if (!mHasFeature) {
+ return;
+ }
+
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ if (admin.mNearbyNotificationStreamingPolicy != policy) {
+ admin.mNearbyNotificationStreamingPolicy = policy;
+ saveSettingsLocked(caller.getUserId());
+ }
+ }
+ }
+
+ @Override
+ public int getNearbyNotificationStreamingPolicy() {
+ if (!mHasFeature) {
+ return NEARBY_STREAMING_DISABLED;
+ }
+
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller)
+ || isProfileOwner(caller)
+ || hasCallingOrSelfPermission(permission.READ_NEARBY_STREAMING_POLICY));
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ return admin.mNearbyNotificationStreamingPolicy;
+ }
+ }
+
+ @Override
+ public void setNearbyAppStreamingPolicy(int policy) {
+ if (!mHasFeature) {
+ return;
+ }
+
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ if (admin.mNearbyAppStreamingPolicy != policy) {
+ admin.mNearbyAppStreamingPolicy = policy;
+ saveSettingsLocked(caller.getUserId());
+ }
+ }
+ }
+
+ @Override
+ public int getNearbyAppStreamingPolicy() {
+ if (!mHasFeature) {
+ return NEARBY_STREAMING_DISABLED;
+ }
+
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller)
+ || isProfileOwner(caller)
+ || hasCallingOrSelfPermission(permission.READ_NEARBY_STREAMING_POLICY));
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ return admin.mNearbyAppStreamingPolicy;
+ }
+ }
+
/**
* Set whether auto time is required by the specified admin (must be device or profile owner).
*/
@@ -16111,7 +16191,6 @@
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- // DO shouldn't be able to use this method.
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
Preconditions.checkState(canHandleCheckPolicyComplianceIntent(caller));
@@ -16142,18 +16221,26 @@
.write();
}
- /** Starts an activity to check policy compliance in the DPC. */
- private void triggerPolicyComplianceCheck(int profileUserId) {
- final Intent intent = new Intent(ACTION_CHECK_POLICY_COMPLIANCE);
+ /** Starts an activity to check policy compliance or request compliance acknowledgement. */
+ private void triggerPolicyComplianceCheckIfNeeded(int profileUserId, boolean suspended) {
synchronized (getLockObject()) {
final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
if (profileOwner == null) {
Slog.wtf(LOG_TAG, "Profile owner not found for compliance check");
return;
}
- intent.setPackage(profileOwner.info.getPackageName());
+ if (suspended) {
+ // If suspended, DPC will need to show an activity.
+ final Intent intent = new Intent(ACTION_CHECK_POLICY_COMPLIANCE);
+ intent.setPackage(profileOwner.info.getPackageName());
+ mContext.startActivityAsUser(intent, UserHandle.of(profileUserId));
+ } else if (profileOwner.mProfileOffDeadline > 0) {
+ // If not suspended, but deadline set, DPC needs to acknowledge compliance so that
+ // the deadline can be reset.
+ sendAdminCommandLocked(profileOwner, ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED,
+ /* adminExtras= */ null, /* receiver= */ null, /* inForeground = */ true);
+ }
}
- mContext.startActivityAsUser(intent, UserHandle.of(profileUserId));
}
/**
@@ -16162,39 +16249,35 @@
*
* @param unlocked whether the profile is currently running unlocked.
*/
- private @PersonalAppsSuspensionReason int updatePersonalAppsSuspension(
- int profileUserId, boolean unlocked) {
- final boolean suspendedExplicitly;
- final boolean suspendedByTimeout;
+ private boolean updatePersonalAppsSuspension(int profileUserId, boolean unlocked) {
+ final boolean shouldSuspend;
synchronized (getLockObject()) {
final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
if (profileOwner != null) {
- final int deadlineState =
- updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked);
- suspendedExplicitly = profileOwner.mSuspendPersonalApps;
- suspendedByTimeout = deadlineState == PROFILE_OFF_DEADLINE_REACHED;
- Slog.d(LOG_TAG, "Personal apps suspended explicitly: %b, deadline state: %d",
- suspendedExplicitly, deadlineState);
final int notificationState =
- unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState;
+ updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked);
+ final boolean suspendedExplicitly = profileOwner.mSuspendPersonalApps;
+ final boolean suspendedByTimeout = profileOwner.mProfileOffDeadline == -1;
+ Slog.d(LOG_TAG,
+ "Personal apps suspended explicitly: %b, by timeout: %b, notification: %d",
+ suspendedExplicitly, suspendedByTimeout, notificationState);
updateProfileOffDeadlineNotificationLocked(
profileUserId, profileOwner, notificationState);
+ shouldSuspend = suspendedExplicitly || suspendedByTimeout;
} else {
- suspendedExplicitly = false;
- suspendedByTimeout = false;
+ shouldSuspend = false;
}
}
final int parentUserId = getProfileParentId(profileUserId);
- suspendPersonalAppsInternal(parentUserId, suspendedExplicitly || suspendedByTimeout);
-
- return makeSuspensionReasons(suspendedExplicitly, suspendedByTimeout);
+ suspendPersonalAppsInternal(parentUserId, shouldSuspend);
+ return shouldSuspend;
}
/**
* Checks work profile time off policy, scheduling personal apps suspension via alarm if
* necessary.
- * @return profile deadline state
+ * @return notification state
*/
private int updateProfileOffDeadlineLocked(
int profileUserId, ActiveAdmin profileOwner, boolean unlocked) {
@@ -16206,7 +16289,7 @@
profileOwner.mProfileOffDeadline = -1;
saveSettingsLocked(profileUserId);
}
- return PROFILE_OFF_DEADLINE_REACHED;
+ return unlocked ? PROFILE_OFF_NOTIFICATION_NONE : PROFILE_OFF_NOTIFICATION_SUSPENDED;
}
boolean shouldSaveSettings = false;
if (profileOwner.mSuspendPersonalApps) {
@@ -16216,8 +16299,8 @@
shouldSaveSettings = true;
}
} else if (profileOwner.mProfileOffDeadline != 0
- && (profileOwner.mProfileMaximumTimeOffMillis == 0 || unlocked)) {
- // There is a deadline but either there is no policy or the profile is unlocked -> clear
+ && (profileOwner.mProfileMaximumTimeOffMillis == 0)) {
+ // There is a deadline but either there is no policy -> clear
// the deadline.
Slog.i(LOG_TAG, "Profile off deadline is reset to zero");
profileOwner.mProfileOffDeadline = 0;
@@ -16236,19 +16319,19 @@
}
final long alarmTime;
- final int deadlineState;
- if (profileOwner.mProfileOffDeadline == 0) {
+ final int notificationState;
+ if (unlocked || profileOwner.mProfileOffDeadline == 0) {
alarmTime = 0;
- deadlineState = PROFILE_OFF_DEADLINE_DEFAULT;
+ notificationState = PROFILE_OFF_NOTIFICATION_NONE;
} else if (profileOwner.mProfileOffDeadline - now < MANAGED_PROFILE_OFF_WARNING_PERIOD) {
// The deadline is close, upon the alarm personal apps should be suspended.
alarmTime = profileOwner.mProfileOffDeadline;
- deadlineState = PROFILE_OFF_DEADLINE_WARNING;
+ notificationState = PROFILE_OFF_NOTIFICATION_WARNING;
} else {
// The deadline is quite far, upon the alarm we should warn the user first, so the
// alarm is scheduled earlier than the actual deadline.
alarmTime = profileOwner.mProfileOffDeadline - MANAGED_PROFILE_OFF_WARNING_PERIOD;
- deadlineState = PROFILE_OFF_DEADLINE_DEFAULT;
+ notificationState = PROFILE_OFF_NOTIFICATION_NONE;
}
final AlarmManager am = mInjector.getAlarmManager();
@@ -16268,7 +16351,7 @@
am.set(AlarmManager.RTC, alarmTime, pi);
}
- return deadlineState;
+ return notificationState;
}
private void suspendPersonalAppsInternal(int userId, boolean suspended) {
@@ -16310,7 +16393,7 @@
@GuardedBy("getLockObject()")
private void updateProfileOffDeadlineNotificationLocked(
int profileUserId, ActiveAdmin profileOwner, int notificationState) {
- if (notificationState == PROFILE_OFF_DEADLINE_DEFAULT) {
+ if (notificationState == PROFILE_OFF_NOTIFICATION_NONE) {
mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED);
return;
}
@@ -16331,7 +16414,7 @@
final String text;
final boolean ongoing;
- if (notificationState == PROFILE_OFF_DEADLINE_WARNING) {
+ if (notificationState == PROFILE_OFF_NOTIFICATION_WARNING) {
// Round to the closest integer number of days.
final int maxDays = (int)
((profileOwner.mProfileMaximumTimeOffMillis + MS_PER_DAY / 2) / MS_PER_DAY);
@@ -16422,7 +16505,6 @@
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
- // DO shouldn't be able to use this method.
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
synchronized (getLockObject()) {
@@ -16432,6 +16514,33 @@
}
@Override
+ public void acknowledgeDeviceCompliant() {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
+ enforceUserUnlocked(caller.getUserId());
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerLocked(caller);
+ if (admin.mProfileOffDeadline > 0) {
+ admin.mProfileOffDeadline = 0;
+ saveSettingsLocked(caller.getUserId());
+ }
+ }
+ }
+
+ @Override
+ public boolean isComplianceAcknowledgementRequired() {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
+ enforceUserUnlocked(caller.getUserId());
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerLocked(caller);
+ return admin.mProfileOffDeadline != 0;
+ }
+ }
+
+ @Override
public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
enforceSystemCaller("call canProfileOwnerResetPasswordWhenLocked");
synchronized (getLockObject()) {
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 94f8e59..6695ba8 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -2523,7 +2523,8 @@
now - mCurrentStatusTs <= Constants::bindingTimeout) {
LOG(INFO) << "Binding still in progress. "
<< (healthy ? "The DL is healthy/freshly bound, ok to retry for a few times."
- : "Already unhealthy, don't do anything.");
+ : "Already unhealthy, don't do anything.")
+ << " for storage " << mId;
// Binding still in progress.
if (!healthy) {
// Already unhealthy, don't do anything.
@@ -2546,7 +2547,8 @@
const auto previousBindTs = mPreviousBindTs;
mPreviousBindTs = now;
- const auto nonCrashingInterval = std::max(castToMs(now - previousBindTs), 100ms);
+ const auto nonCrashingInterval =
+ std::max(castToMs(now - previousBindTs - mPreviousBindDelay), 100ms);
if (previousBindTs.time_since_epoch() == Clock::duration::zero() ||
nonCrashingInterval > Constants::healthyDataLoaderUptime) {
mPreviousBindDelay = 0ms;
@@ -2575,7 +2577,8 @@
const auto bindDelay = *maybeBindDelay;
if (bindDelay > 1s) {
LOG(INFO) << "Delaying bind to " << mParams.packageName << " by "
- << bindDelay.count() / 1000 << "s";
+ << bindDelay.count() / 1000 << "s"
+ << " for storage " << mId;
}
bool result = false;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index ddb7784..1ec446d 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -245,6 +245,7 @@
mId = mountId;
mListener = listener;
mDataLoader = mDataLoaderHolder;
+ mBindDelayMs = bindDelayMs;
*_aidl_return = true;
if (mListener) {
mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_BOUND);
@@ -341,14 +342,18 @@
}
mDataLoader = nullptr;
}
+ mBindDelayMs = -1;
if (mListener) {
mListener->onStatusChanged(id, IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
}
return binder::Status::ok();
}
+ int bindDelayMs() const { return mBindDelayMs; }
+
private:
- int mId;
+ int mId = -1;
+ int mBindDelayMs = -1;
sp<IDataLoaderStatusListener> mListener;
sp<IDataLoader> mDataLoader;
sp<IDataLoader> mDataLoaderHolder;
@@ -604,11 +609,14 @@
MOCK_CONST_METHOD0(now, TimePoint());
void start() { ON_CALL(*this, now()).WillByDefault(Invoke(this, &MockClockWrapper::getClock)); }
+
template <class Delta>
void advance(Delta delta) {
mClock += delta;
}
+ void advanceMs(int deltaMs) { mClock += std::chrono::milliseconds(deltaMs); }
+
TimePoint getClock() const { return mClock; }
TimePoint mClock = Clock::now();
@@ -894,31 +902,38 @@
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith1sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith10sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith100sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith1000sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+ // Try the reduced delay, just in case.
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs() / 2);
mDataLoaderManager->setDataLoaderStatusDestroyed();
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
}
@@ -1012,31 +1027,37 @@
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith1sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith10sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith100sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith1000sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(mDataLoaderManager,
&MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+ mClock->advanceMs(mDataLoaderManager->bindDelayMs());
mDataLoaderManager->setDataLoaderStatusDestroyed();
}
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 29aedce..1208ecc 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -30,6 +30,8 @@
import android.os.ServiceManager;
import android.os.UpdateEngine;
import android.os.UpdateEngineCallback;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.DeviceConfig;
import android.util.Log;
@@ -301,10 +303,18 @@
new Thread(() -> {
try {
- String reportPath = mIProfcollect.report();
+ String reportUuid = mIProfcollect.report();
+
if (!uploadReport) {
return;
}
+
+ final int profileId = getBBProfileId();
+ mIProfcollect.copy_report_to_bb(profileId, reportUuid);
+ String reportPath =
+ "/data/user/" + profileId
+ + "/com.google.android.apps.internal.betterbug/cache/"
+ + reportUuid + ".zip";
Intent uploadIntent =
new Intent("com.google.android.apps.betterbug.intent.action.UPLOAD_PROFILE")
.setPackage("com.google.android.apps.internal.betterbug")
@@ -316,9 +326,27 @@
if (context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0) != null) {
context.sendBroadcast(uploadIntent);
}
+ mIProfcollect.delete_report(reportUuid);
} catch (RemoteException e) {
Log.e(LOG_TAG, e.getMessage());
}
}).start();
}
+
+ /**
+ * Get BetterBug's profile ID. It is the work profile ID, if it exists. Otherwise the system
+ * user ID.
+ *
+ * @return BetterBug's profile ID.
+ */
+ private int getBBProfileId() {
+ UserManager userManager = UserManager.get(getContext());
+ int[] profiles = userManager.getProfileIds(UserHandle.USER_SYSTEM, false);
+ for (int p : profiles) {
+ if (userManager.getUserInfo(p).isManagedProfile()) {
+ return p;
+ }
+ }
+ return UserHandle.USER_SYSTEM;
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 1b0a305..537a49e 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -417,7 +417,7 @@
allowQueryAll.set(true)
- runMethod(target, NON_VERIFIER_UID)
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
}
private fun approvedVerifier() {
@@ -816,7 +816,7 @@
// System/shell only
INTERNAL,
- // INTERNAL || domain verification agent || user setting permission holder
+ // INTERNAL || non-legacy domain verification agent
QUERENT,
// INTERNAL || domain verification agent
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 28940b3..8481961 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -21,6 +21,7 @@
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
+import static android.app.AlarmManager.FLAG_PRIORITIZE;
import static android.app.AlarmManager.FLAG_STANDALONE;
import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
import static android.app.AlarmManager.RTC;
@@ -62,6 +63,7 @@
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_WINDOW;
+import static com.android.server.alarm.AlarmManagerService.Constants.KEY_PRIORITY_ALARM_DELAY;
import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
@@ -443,6 +445,12 @@
TEST_CALLING_UID);
}
+ private void setPrioritizedAlarm(int type, long triggerTime, IAlarmListener listener) {
+ mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
+ FLAG_STANDALONE | FLAG_PRIORITIZE, null, null, TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE, null);
+ }
+
private void setAllowWhileIdleAlarm(int type, long triggerTime, PendingIntent pi,
boolean unrestricted) {
final int flags = unrestricted ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : FLAG_ALLOW_WHILE_IDLE;
@@ -579,6 +587,7 @@
setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 40);
setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 45);
setDeviceConfigLong(KEY_MIN_WINDOW, 50);
+ setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, 55);
assertEquals(5, mService.mConstants.MIN_FUTURITY);
assertEquals(10, mService.mConstants.MIN_INTERVAL);
assertEquals(15, mService.mConstants.MAX_INTERVAL);
@@ -589,6 +598,7 @@
assertEquals(40, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
assertEquals(45, mService.mConstants.LISTENER_TIMEOUT);
assertEquals(50, mService.mConstants.MIN_WINDOW);
+ assertEquals(55, mService.mConstants.PRIORITY_ALARM_DELAY);
}
@Test
@@ -1586,6 +1596,89 @@
}
@Test
+ public void prioritizedAlarmsInBatterySaver() throws Exception {
+ when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(true);
+ when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
+ final long minDelay = 5;
+ setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, minDelay);
+
+ final long firstTrigger = mNowElapsedTest + 4;
+ final AtomicInteger alarmsFired = new AtomicInteger(0);
+ final int numAlarms = 10;
+ for (int i = 0; i < numAlarms; i++) {
+ setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
+ new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(IAlarmCompleteListener callback)
+ throws RemoteException {
+ alarmsFired.incrementAndGet();
+ }
+ });
+ }
+ assertEquals(firstTrigger, mTestTimer.getElapsed());
+ mNowElapsedTest = firstTrigger;
+ mTestTimer.expire();
+ while (alarmsFired.get() < numAlarms) {
+ assertEquals(mNowElapsedTest + minDelay, mTestTimer.getElapsed());
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ assertEquals(numAlarms, alarmsFired.get());
+ }
+
+ @Test
+ public void prioritizedAlarmsInDeviceIdle() throws Exception {
+ doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+ final long minDelay = 5;
+ setDeviceConfigLong(KEY_PRIORITY_ALARM_DELAY, minDelay);
+
+ final long idleUntil = mNowElapsedTest + 1000;
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil, getNewMockPendingIntent());
+ assertNotNull(mService.mPendingIdleUntil);
+
+ final long firstTrigger = mNowElapsedTest + 4;
+ final AtomicInteger alarmsFired = new AtomicInteger(0);
+ final int numAlarms = 10;
+ for (int i = 0; i < numAlarms; i++) {
+ setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
+ new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(IAlarmCompleteListener callback)
+ throws RemoteException {
+ alarmsFired.incrementAndGet();
+ }
+ });
+ }
+ assertEquals(firstTrigger, mTestTimer.getElapsed());
+ mNowElapsedTest = firstTrigger;
+ mTestTimer.expire();
+ while (alarmsFired.get() < numAlarms) {
+ assertEquals(mNowElapsedTest + minDelay, mTestTimer.getElapsed());
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ assertEquals(numAlarms, alarmsFired.get());
+
+ setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 3, new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
+ }
+ });
+ setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 2, new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
+ }
+ });
+ assertEquals(idleUntil - 3, mTestTimer.getElapsed());
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+
+ assertEquals(idleUntil, mTestTimer.getElapsed());
+ }
+
+ @Test
public void dispatchOrder() throws Exception {
doReturn(0).when(mService).fuzzForDuration(anyLong());
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 7a3a950..647ea13 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -16,8 +16,6 @@
package com.android.server.appop;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
-import static android.app.AppOpsManager.FILTER_BY_UID;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
@@ -44,7 +42,6 @@
import static org.mockito.ArgumentMatchers.nullable;
import android.app.ActivityManager;
-import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
import android.content.ContentResolver;
@@ -53,7 +50,6 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
-import android.os.RemoteCallback;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
@@ -66,6 +62,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.quality.Strictness;
@@ -73,9 +70,6 @@
import java.io.File;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
/**
* Unit tests for AppOpsService. Covers functionality that is difficult to test using CTS tests
@@ -229,6 +223,7 @@
// Tests that ops are persisted during shutdown.
@Test
+ @Ignore("b/183523911")
public void testShutdown() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 3870b02..1b8f9c7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -19,6 +19,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -41,10 +42,11 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -53,8 +55,6 @@
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.NetworkPolicyManager;
import android.os.Build;
import android.os.Looper;
@@ -136,7 +136,7 @@
}
@Test
- public void testInsane() throws Exception {
+ public void testUsable() throws Exception {
final Network net = new Network(101);
final JobInfo.Builder job = createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1),
@@ -192,6 +192,30 @@
}
@Test
+ public void testInsane() throws Exception {
+ final Network net = new Network(101);
+ final JobInfo.Builder job = createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1),
+ DataUnit.MEBIBYTES.toBytes(1))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+
+ final ConnectivityController controller = new ConnectivityController(mService);
+ when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L);
+
+
+ // Suspended networks aren't usable.
+ assertFalse(controller.isSatisfied(createJobStatus(job), net,
+ createCapabilities().removeCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ .setLinkUpstreamBandwidthKbps(1024).setLinkDownstreamBandwidthKbps(1024),
+ mConstants));
+
+ // Not suspended networks are usable.
+ assertTrue(controller.isSatisfied(createJobStatus(job), net,
+ createCapabilities().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(1024), mConstants));
+ }
+
+ @Test
public void testCongestion() throws Exception {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final JobInfo.Builder job = createJob()
@@ -263,9 +287,17 @@
@Test
public void testUpdates() throws Exception {
- final ArgumentCaptor<NetworkCallback> callback = ArgumentCaptor
- .forClass(NetworkCallback.class);
- doNothing().when(mConnManager).registerNetworkCallback(any(), callback.capture());
+ final ArgumentCaptor<NetworkCallback> callbackCaptor =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ doNothing().when(mConnManager).registerNetworkCallback(any(), callbackCaptor.capture());
+ final ArgumentCaptor<NetworkCallback> redCallbackCaptor =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ doNothing().when(mConnManager).registerDefaultNetworkCallbackAsUid(
+ eq(UID_RED), redCallbackCaptor.capture(), any());
+ final ArgumentCaptor<NetworkCallback> blueCallbackCaptor =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ doNothing().when(mConnManager).registerDefaultNetworkCallbackAsUid(
+ eq(UID_BLUE), blueCallbackCaptor.capture(), any());
final ConnectivityController controller = new ConnectivityController(mService);
@@ -281,15 +313,16 @@
final JobStatus blue = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+ controller.maybeStartTrackingJobLocked(red, null);
+ controller.maybeStartTrackingJobLocked(blue, null);
+ final NetworkCallback generalCallback = callbackCaptor.getValue();
+ final NetworkCallback redCallback = redCallbackCaptor.getValue();
+ final NetworkCallback blueCallback = blueCallbackCaptor.getValue();
// Pretend we're offline when job is added
{
- reset(mConnManager);
- answerNetwork(UID_RED, null, null);
- answerNetwork(UID_BLUE, null, null);
-
- controller.maybeStartTrackingJobLocked(red, null);
- controller.maybeStartTrackingJobLocked(blue, null);
+ answerNetwork(generalCallback, redCallback, null, null, null);
+ answerNetwork(generalCallback, blueCallback, null, null, null);
assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
assertFalse(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
@@ -297,11 +330,10 @@
// Metered network
{
- reset(mConnManager);
- answerNetwork(UID_RED, meteredNet, meteredCaps);
- answerNetwork(UID_BLUE, meteredNet, meteredCaps);
+ answerNetwork(generalCallback, redCallback, null, meteredNet, meteredCaps);
+ answerNetwork(generalCallback, blueCallback, null, meteredNet, meteredCaps);
- callback.getValue().onCapabilitiesChanged(meteredNet, meteredCaps);
+ generalCallback.onCapabilitiesChanged(meteredNet, meteredCaps);
assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
@@ -309,11 +341,10 @@
// Unmetered network background
{
- reset(mConnManager);
- answerNetwork(UID_RED, meteredNet, meteredCaps);
- answerNetwork(UID_BLUE, meteredNet, meteredCaps);
+ answerNetwork(generalCallback, redCallback, meteredNet, meteredNet, meteredCaps);
+ answerNetwork(generalCallback, blueCallback, meteredNet, meteredNet, meteredCaps);
- callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
+ generalCallback.onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
@@ -321,11 +352,10 @@
// Lost metered network
{
- reset(mConnManager);
- answerNetwork(UID_RED, unmeteredNet, unmeteredCaps);
- answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
+ answerNetwork(generalCallback, redCallback, meteredNet, unmeteredNet, unmeteredCaps);
+ answerNetwork(generalCallback, blueCallback, meteredNet, unmeteredNet, unmeteredCaps);
- callback.getValue().onLost(meteredNet);
+ generalCallback.onLost(meteredNet);
assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
@@ -333,11 +363,10 @@
// Specific UID was blocked
{
- reset(mConnManager);
- answerNetwork(UID_RED, null, null);
- answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
+ answerNetwork(generalCallback, redCallback, unmeteredNet, null, null);
+ answerNetwork(generalCallback, blueCallback, unmeteredNet, unmeteredNet, unmeteredCaps);
- callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
+ generalCallback.onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
@@ -404,6 +433,8 @@
final JobStatus blue = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+ controller.maybeStartTrackingJobLocked(red, null);
+ controller.maybeStartTrackingJobLocked(blue, null);
InOrder inOrder = inOrder(mNetPolicyManagerInternal);
@@ -560,6 +591,18 @@
@Test
public void testRestrictedJobTracking() {
+ final ArgumentCaptor<NetworkCallback> callback =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ doNothing().when(mConnManager).registerNetworkCallback(any(), callback.capture());
+ final ArgumentCaptor<NetworkCallback> redCallback =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ doNothing().when(mConnManager).registerDefaultNetworkCallbackAsUid(
+ eq(UID_RED), redCallback.capture(), any());
+ final ArgumentCaptor<NetworkCallback> blueCallback =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ doNothing().when(mConnManager).registerDefaultNetworkCallbackAsUid(
+ eq(UID_BLUE), blueCallback.capture(), any());
+
final JobStatus networked = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_CELLULAR), UID_RED);
@@ -570,13 +613,11 @@
final Network cellularNet = new Network(101);
final NetworkCapabilities cellularCaps =
createCapabilities().addTransportType(TRANSPORT_CELLULAR);
- reset(mConnManager);
- answerNetwork(UID_RED, cellularNet, cellularCaps);
- answerNetwork(UID_BLUE, cellularNet, cellularCaps);
final ConnectivityController controller = new ConnectivityController(mService);
controller.maybeStartTrackingJobLocked(networked, null);
controller.maybeStartTrackingJobLocked(unnetworked, null);
+ answerNetwork(callback.getValue(), redCallback.getValue(), null, cellularNet, cellularCaps);
assertTrue(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
@@ -600,18 +641,28 @@
assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
}
- private void answerNetwork(int uid, Network net, NetworkCapabilities caps) {
- when(mConnManager.getActiveNetworkForUid(eq(uid), anyBoolean())).thenReturn(net);
- when(mConnManager.getNetworkCapabilities(eq(net))).thenReturn(caps);
- if (net != null) {
- final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, null, null);
- ni.setDetailedState(DetailedState.CONNECTED, null, null);
- when(mConnManager.getNetworkInfoForUid(eq(net), eq(uid), anyBoolean())).thenReturn(ni);
+ private void answerNetwork(@NonNull NetworkCallback generalCallback,
+ @Nullable NetworkCallback uidCallback, @Nullable Network lastNetwork,
+ @Nullable Network net, @Nullable NetworkCapabilities caps) {
+ if (net == null) {
+ generalCallback.onLost(lastNetwork);
+ if (uidCallback != null) {
+ uidCallback.onLost(lastNetwork);
+ }
+ } else {
+ generalCallback.onAvailable(net);
+ generalCallback.onCapabilitiesChanged(net, caps);
+ if (uidCallback != null) {
+ uidCallback.onAvailable(net);
+ uidCallback.onBlockedStatusChanged(net, false);
+ uidCallback.onCapabilitiesChanged(net, caps);
+ }
}
}
private static NetworkCapabilities createCapabilities() {
return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
.addCapability(NET_CAPABILITY_VALIDATED);
}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 5761958..9513c6e 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -70,6 +70,7 @@
<uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
<uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
<uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
<uses-permission android:name="android.permission.HARDWARE_TEST"/>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 29691fb..502f64a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -16,7 +16,7 @@
package com.android.server.accessibility.magnification;
-import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationRequestObserver;
+import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -96,8 +96,8 @@
final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
private final MagnificationAnimationCallback mAnimationCallback = mock(
MagnificationAnimationCallback.class);
- private final MagnificationRequestObserver mRequestObserver = mock(
- MagnificationRequestObserver.class);
+ private final MagnificationInfoChangedCallback mRequestObserver = mock(
+ MagnificationInfoChangedCallback.class);
final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null);
ValueAnimator mMockValueAnimator;
@@ -1145,6 +1145,15 @@
verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(false));
}
+ @Test
+ public void testImeWindowIsShown_serviceNotified() {
+ register(DISPLAY_0);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(DISPLAY_0);
+ callbacks.onImeWindowVisibilityChanged(true);
+ mMessageCapturingHandler.sendAllMessages();
+ verify(mRequestObserver).onImeWindowVisibilityChanged(eq(true));
+ }
+
private void setScaleToMagnifying() {
register(DISPLAY_0);
float scale = 2.0f;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 28a6ff7..f881f04 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -53,7 +53,7 @@
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.EventStreamTransformation;
-import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationRequestObserver;
+import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
import com.android.server.wm.WindowManagerInternal;
@@ -126,7 +126,7 @@
@Mock
MagnificationGestureHandler.Callback mMockCallback;
@Mock
- MagnificationRequestObserver mMagnificationRequestObserver;
+ MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
@Mock
WindowMagnificationPromptController mWindowMagnificationPromptController;
@@ -151,7 +151,7 @@
when(mockController.getAnimationDuration()).thenReturn(1000L);
when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true);
mFullScreenMagnificationController = new FullScreenMagnificationController(mockController,
- new Object(), mMagnificationRequestObserver) {
+ new Object(), mMagnificationInfoChangedCallback) {
@Override
public boolean magnificationRegionContains(int displayId, float x, float y) {
return true;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index cf23197..3dff36e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -486,6 +486,47 @@
eq(MODE_WINDOW));
}
+ @Test
+ public void imeWindowStateShown_windowMagnifying_logWindowMode() {
+ mMagnificationController.onWindowMagnificationActivationState(true);
+
+ mMagnificationController.onImeWindowVisibilityChanged(true);
+
+ verify(mMagnificationController).logMagnificationModeWithIme(
+ eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
+ }
+
+ @Test
+ public void imeWindowStateShown_fullScreenMagnifying_logFullScreenMode() {
+ mMagnificationController.onFullScreenMagnificationActivationState(true);
+
+ mMagnificationController.onImeWindowVisibilityChanged(true);
+
+ verify(mMagnificationController).logMagnificationModeWithIme(
+ eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
+ }
+
+ @Test
+ public void imeWindowStateShown_noMagnifying_noLogAnyMode() {
+ mMagnificationController.onImeWindowVisibilityChanged(true);
+
+ verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+ }
+
+ @Test
+ public void imeWindowStateHidden_windowMagnifying_noLogAnyMode() {
+ mMagnificationController.onFullScreenMagnificationActivationState(true);
+
+ verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+ }
+
+ @Test
+ public void imeWindowStateHidden_fullScreenMagnifying_noLogAnyMode() {
+ mMagnificationController.onWindowMagnificationActivationState(true);
+
+ verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+ }
+
private void setMagnificationEnabled(int mode) throws RemoteException {
setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
index f3ee233..4240581 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -104,12 +104,12 @@
// Insert package1 document
GenericDocument document1 =
- new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
// Insert package2 document
GenericDocument document2 =
- new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
mAppSearchImpl.putDocument("package2", "database2", document2, /*logger=*/ null);
// No query filters specified, global query can retrieve all documents.
@@ -122,8 +122,8 @@
// Document2 will be first since it got indexed later and has a "better", aka more recent
// score.
- assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
- assertThat(searchResultPage.getResults().get(1).getDocument()).isEqualTo(document1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+ assertThat(searchResultPage.getResults().get(1).getGenericDocument()).isEqualTo(document1);
}
/**
@@ -158,12 +158,12 @@
// Insert package1 document
GenericDocument document1 =
- new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
// Insert package2 document
GenericDocument document2 =
- new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
mAppSearchImpl.putDocument("package2", "database2", document2, /*logger=*/ null);
// "package1" filter specified
@@ -176,7 +176,7 @@
mAppSearchImpl.globalQuery(
"", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
// "package2" filter specified
searchSpec =
@@ -188,7 +188,7 @@
mAppSearchImpl.globalQuery(
"", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
}
@Test
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 fcd6b84..7bdc87e 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -116,7 +116,7 @@
}
CompatConfigBuilder addOverridableChangeWithId(long id) {
- mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", true));
+ mChanges.add(new CompatChange(id, "", -1, -1, true, 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 bd77405..a866363 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -260,6 +260,36 @@
}
@Test
+ public void testInstallerCanSetOverrides() throws Exception {
+ final long changeId = 1234L;
+ final int installerUid = 23;
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addOverridableChangeWithId(1234L)
+ .build();
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package")
+ .build();
+ PackageManager packageManager = mock(PackageManager.class);
+ when(mContext.getPackageManager()).thenReturn(packageManager);
+ when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(applicationInfo);
+
+ // Force the validator to prevent overriding the change by using a user build.
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+ CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(
+ Collections.singletonMap(1234L,
+ new PackageOverride.Builder()
+ .setMaxVersionCode(99L)
+ .setEnabled(true)
+ .build()));
+
+ compatConfig.addOverrides(config, "com.some.package");
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+ }
+
+ @Test
public void testApplyDeferredOverridesAfterInstallingApp() throws Exception {
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
.withPackageName("com.notinstalled.foo")
@@ -639,9 +669,18 @@
.build());
when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
.thenThrow(new NameNotFoundException());
-
- compatConfig.addOverride(1L, "foo.bar", true);
- compatConfig.addOverride(2L, "bar.baz", false);
+ compatConfig.addOverrides(
+ new CompatibilityOverrideConfig(
+ Collections.singletonMap(
+ 1L,
+ new PackageOverride.Builder().setEnabled(true).build())),
+ "foo.bar");
+ compatConfig.addOverrides(
+ new CompatibilityOverrideConfig(
+ Collections.singletonMap(
+ 2L,
+ new PackageOverride.Builder().setEnabled(false).build())),
+ "bar.baz");
assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<overrides>\n"
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 3fc6e99..a2664e5 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -113,7 +113,7 @@
new CompatibilityChangeInfo(
6L, "", Build.VERSION_CODES.R, -1, false, false, "", false),
new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "", false),
- new CompatibilityChangeInfo(8L, "", -1, -1, false, true, "", true));
+ new CompatibilityChangeInfo(8L, "", -1, -1, true, false, "", true));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 15ada89..c8099e2 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -464,6 +464,40 @@
}
@Test
+ public void testStaleAppRequestSize() {
+ DisplayModeDirector director =
+ new DisplayModeDirector(mContext, mHandler, mInjector);
+ Display.Mode[] modes = new Display.Mode[] {
+ new Display.Mode(1, 1280, 720, 60),
+ };
+ Display.Mode defaultMode = modes[0];
+
+ // Inject supported modes
+ SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
+ supportedModesByDisplay.put(DISPLAY_ID, modes);
+ director.injectSupportedModesByDisplay(supportedModesByDisplay);
+
+ // Inject default mode
+ SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
+ defaultModesByDisplay.put(DISPLAY_ID, defaultMode);
+ director.injectDefaultModeByDisplay(defaultModesByDisplay);
+
+ // Inject votes
+ SparseArray<Vote> votes = new SparseArray<>();
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(1920, 1080));
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(50, 50));
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ director.injectVotesByDisplay(votesByDisplay);
+
+ director.setShouldAlwaysRespectAppRequestedMode(true);
+
+ // We should return the only available mode
+ DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(specs.baseModeId).isEqualTo(defaultMode.getModeId());
+ }
+
+ @Test
public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index dcb2c15..bcd853c 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -186,14 +186,11 @@
LogicalDisplay display2 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0));
LogicalDisplay display3 = add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0));
- // Physical displays should be automatically put into the default group.
assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
-
- // Virtual displays should belong to no group by default.
- assertEquals(Display.INVALID_DISPLAY_GROUP,
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
}
@@ -215,13 +212,13 @@
assertNotEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
- // Now switch it to the invalid group by removing the flag and issuing an update
+ // Now switch it back to the default group by removing the flag and issuing an update
DisplayDeviceInfo info = device3.getSourceInfo();
info.flags = info.flags & ~DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
- // Verify the virtual display has not been placed into a group.
- assertEquals(Display.INVALID_DISPLAY_GROUP,
+ // Verify the new group is correct.
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
}
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index eebc25a..6f1268e 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.content.Context;
@@ -34,6 +35,7 @@
import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
+import android.util.ArrayMap;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
@@ -48,6 +50,7 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -793,6 +796,97 @@
}
}
+ @Test
+ public void testChooseSystemVoiceIme() throws Exception {
+ final InputMethodInfo systemIme = createFakeInputMethodInfo("SystemIme", "fake.voice0",
+ true /* isSystem */);
+
+ // Returns null when the config value is null.
+ {
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ methodMap.put(systemIme.getId(), systemIme);
+ assertNull(InputMethodUtils.chooseSystemVoiceIme(methodMap, null, ""));
+ }
+
+ // Returns null when the config value is empty.
+ {
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ methodMap.put(systemIme.getId(), systemIme);
+ assertNull(InputMethodUtils.chooseSystemVoiceIme(methodMap, "", ""));
+ }
+
+ // Returns null when the configured package doesn't have an IME.
+ {
+ assertNull(InputMethodUtils.chooseSystemVoiceIme(new ArrayMap<>(),
+ systemIme.getPackageName(), ""));
+ }
+
+ // Returns the right one when the current default is null.
+ {
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ methodMap.put(systemIme.getId(), systemIme);
+ assertEquals(systemIme, InputMethodUtils.chooseSystemVoiceIme(methodMap,
+ systemIme.getPackageName(), null));
+ }
+
+ // Returns the right one when the current default is empty.
+ {
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ methodMap.put(systemIme.getId(), systemIme);
+ assertEquals(systemIme, InputMethodUtils.chooseSystemVoiceIme(methodMap,
+ systemIme.getPackageName(), ""));
+ }
+
+ // Returns null when the current default isn't found.
+ {
+ assertNull(InputMethodUtils.chooseSystemVoiceIme(new ArrayMap<>(),
+ systemIme.getPackageName(), systemIme.getId()));
+ }
+
+ // Returns null when there are multiple IMEs defined by the config package.
+ {
+ final InputMethodInfo secondIme = createFakeInputMethodInfo(systemIme.getPackageName(),
+ "fake.voice1", true /* isSystem */);
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ methodMap.put(systemIme.getId(), systemIme);
+ methodMap.put(secondIme.getId(), secondIme);
+ assertNull(InputMethodUtils.chooseSystemVoiceIme(methodMap, systemIme.getPackageName(),
+ ""));
+ }
+
+ // Returns the current one when the current default and config point to the same package.
+ {
+ final InputMethodInfo secondIme = createFakeInputMethodInfo("SystemIme", "fake.voice1",
+ true /* isSystem */);
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ methodMap.put(systemIme.getId(), systemIme);
+ methodMap.put(secondIme.getId(), secondIme);
+ assertEquals(systemIme, InputMethodUtils.chooseSystemVoiceIme(methodMap,
+ systemIme.getPackageName(), systemIme.getId()));
+ }
+
+ // Doesn't return the current default if it isn't a system app.
+ {
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ final InputMethodInfo nonSystemIme = createFakeInputMethodInfo("NonSystemIme",
+ "fake.voice0", false /* isSystem */);
+ methodMap.put(nonSystemIme.getId(), nonSystemIme);
+ assertNull(InputMethodUtils.chooseSystemVoiceIme(methodMap,
+ nonSystemIme.getPackageName(), nonSystemIme.getId()));
+ }
+
+ // Returns null if the configured one isn't a system app.
+ {
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ final InputMethodInfo nonSystemIme = createFakeInputMethodInfo(
+ "FakeDefaultAutoVoiceIme", "fake.voice0", false /* isSystem */);
+ methodMap.put(systemIme.getId(), systemIme);
+ methodMap.put(nonSystemIme.getId(), nonSystemIme);
+ assertNull(InputMethodUtils.chooseSystemVoiceIme(methodMap,
+ nonSystemIme.getPackageName(), ""));
+ }
+ }
+
private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes,
final Locale systemLocale, String... expectedImeNames) {
final Context context = createTargetContextWithLocales(new LocaleList(systemLocale));
@@ -866,6 +960,25 @@
}
private static InputMethodInfo createFakeInputMethodInfo(String packageName, String name,
+ boolean isSystem) {
+ final ResolveInfo ri = new ResolveInfo();
+ final ServiceInfo si = new ServiceInfo();
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = packageName;
+ ai.enabled = true;
+ if (isSystem) {
+ ai.flags |= ApplicationInfo.FLAG_SYSTEM;
+ }
+ si.applicationInfo = ai;
+ si.enabled = true;
+ si.packageName = packageName;
+ si.name = name;
+ si.exported = true;
+ ri.serviceInfo = si;
+ return new InputMethodInfo(ri, false, "", Collections.emptyList(), 1, true);
+ }
+
+ private static InputMethodInfo createFakeInputMethodInfo(String packageName, String name,
CharSequence label, boolean isAuxIme, boolean isDefault,
List<InputMethodSubtype> subtypes) {
final ResolveInfo ri = new ResolveInfo();
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 212a9c6..88b0651 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -43,6 +43,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.IUidObserver;
+import android.app.PendingIntent;
import android.app.Person;
import android.app.admin.DevicePolicyManager;
import android.app.appsearch.AppSearchBatchResult;
@@ -54,12 +55,14 @@
import android.app.appsearch.IAppSearchResultCallback;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResultPage;
+import android.app.appsearch.SetSchemaResponse;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
@@ -89,6 +92,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
@@ -197,6 +201,13 @@
}
@Override
+ public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
+ throws PackageManager.NameNotFoundException {
+ // ignore.
+ return this;
+ }
+
+ @Override
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
IntentFilter filter, String broadcastPermission, Handler scheduler) {
// ignore.
@@ -619,6 +630,12 @@
boolean injectHasInteractAcrossUsersFullPermission(int callingPid, int callingUid) {
return false;
}
+
+ @Override
+ PendingIntent injectCreatePendingIntent(Context context, int requestCode,
+ @NonNull Intent[] intents, int flags, Bundle options, UserHandle user) {
+ return new PendingIntent(mock(IIntentSender.class));
+ }
}
protected class LauncherAppsTestable extends LauncherApps {
@@ -656,7 +673,8 @@
packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
}
}
- callback.onResult(AppSearchResult.newSuccessfulResult(null));
+ final SetSchemaResponse response = new SetSchemaResponse.Builder().build();
+ callback.onResult(AppSearchResult.newSuccessfulResult(response.getBundle()));
}
@Override
@@ -769,6 +787,21 @@
}
@Override
+ public void writeQueryResultsToFile(String packageName, String databaseName,
+ ParcelFileDescriptor fileDescriptor, String queryExpression,
+ Bundle searchSpecBundle, int userId, IAppSearchResultCallback callback)
+ throws RemoteException {
+ ignore(callback);
+ }
+
+ @Override
+ public void putDocumentsFromFile(String packageName, String databaseName,
+ ParcelFileDescriptor fileDescriptor, int userId, IAppSearchResultCallback callback)
+ throws RemoteException {
+ ignore(callback);
+ }
+
+ @Override
public void reportUsage(String packageName, String databaseName, String namespace,
String uri, long usageTimeMillis, boolean systemUsage, int userId,
IAppSearchResultCallback callback)
@@ -817,6 +850,12 @@
}
@Override
+ public void getStorageInfo(String packageName, String databaseName, int userId,
+ IAppSearchResultCallback callback) throws RemoteException {
+ ignore(callback);
+ }
+
+ @Override
public void persistToDisk(int userId) throws RemoteException {
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index d63a467..a231169 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -287,7 +287,7 @@
final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
.setIcon(0x11220001)
- .setTitle(0x11220002)
+ .setTitle("String Title")
.setMessage("1st message")
.setNeutralButtonText(0x11220003)
.setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS)
@@ -296,7 +296,7 @@
.setIcon(0x22220001)
.setTitle(0x22220002)
.setMessage("2nd message")
- .setNeutralButtonText(0x22220003)
+ .setNeutralButtonText("String button text")
.setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND)
.build();
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index b17085e..fd3e7a8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -15,8 +15,13 @@
*/
package com.android.server.pm;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+
+import android.app.PendingIntent;
import android.app.appsearch.PackageIdentifier;
import android.content.pm.AppSearchShortcutInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
import java.util.Random;
@@ -41,4 +46,18 @@
new PackageIdentifier(CALLING_PACKAGE_2, cert)));
});
}
+
+ public void testGetShortcutIntents_ReturnsMutablePendingIntents() throws RemoteException {
+ setDefaultLauncher(USER_0, LAUNCHER_1);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () ->
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))))
+ );
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ final PendingIntent intent = mLauncherApps.getShortcutIntent(
+ CALLING_PACKAGE_1, "s1", null, UserHandle.SYSTEM);
+ assertNotNull(intent);
+ });
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
index 322e448..826a8d4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
@@ -57,7 +57,7 @@
}
@Test
- public void equalsComparesTitle() {
+ public void equalsComparesTitleIds() {
final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
@@ -67,7 +67,39 @@
}
@Test
- public void equalsComparesButtonText() {
+ public void equalsIgnoresTitleStringsWhenIdsSet() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+ .setTitle(VALID_TEST_RES_ID_1)
+ .setTitle("1st title");
+ final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+ .setTitle(VALID_TEST_RES_ID_1)
+ .setTitle("2nd title");
+ // String titles different but should get be ignored when resource ids are set
+ assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void equalsComparesTitleStringsWhenNoIdsSet() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+ .setTitle("1st title");
+ final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+ .setTitle("2nd title");
+ // Both have different titles, which are not ignored as resource ids aren't set
+ assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void titleStringClearedWhenResIdSet() {
+ final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+ .setTitle(VALID_TEST_RES_ID_2)
+ .setTitle("Should be cleared on build")
+ .build();
+ assertNull(dialogInfo.getTitle());
+ assertEquals(VALID_TEST_RES_ID_2, dialogInfo.getTitleResId());
+ }
+
+ @Test
+ public void equalsComparesButtonTextIds() {
final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
@@ -77,6 +109,38 @@
}
@Test
+ public void equalsIgnoresButtonStringsWhenIdsSet() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+ .setNeutralButtonText(VALID_TEST_RES_ID_1)
+ .setNeutralButtonText("1st button text");
+ final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+ .setNeutralButtonText(VALID_TEST_RES_ID_1)
+ .setNeutralButtonText("2nd button text");
+ // Button strings different but should get be ignored when resource ids are set
+ assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void equalsComparesButtonStringsWhenNoIdsSet() {
+ final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+ .setNeutralButtonText("1st button text");
+ final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+ .setNeutralButtonText("2nd button text");
+ // Both have different button texts, which are not ignored as resource ids aren't set
+ assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+ }
+
+ @Test
+ public void buttonStringClearedWhenResIdSet() {
+ final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+ .setNeutralButtonText(VALID_TEST_RES_ID_2)
+ .setNeutralButtonText("Should be cleared on build")
+ .build();
+ assertNull(dialogInfo.getNeutralButtonText());
+ assertEquals(VALID_TEST_RES_ID_2, dialogInfo.getNeutralButtonTextResId());
+ }
+
+ @Test
public void equalsComparesButtonAction() {
final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
index 13d75a7..f014119 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
@@ -22,6 +22,7 @@
import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -49,6 +50,9 @@
private static final String COMPILER_FILTER = "space-profile";
private static final String PROFILE_DEX_METADATA = "primary.prof";
private static final String VDEX_DEX_METADATA = "primary.vdex";
+ private static final String INSTRUCTION_SET = "arm64";
+ private static final String BASE_APK = "base.apk";
+ private static final String SPLIT_APK = "split.apk";
private static final byte[] DEX_CONTENT = "dexData".getBytes();
private static final int COMPILATION_REASON = 1;
private static final int RESULT_CODE = 222;
@@ -97,17 +101,18 @@
ArtStatsLogUtils.writeStatsLog(
mockLogger,
SESSION_ID,
- apk.toString(),
COMPILER_FILTER,
UID,
COMPILE_TIME,
dexMetadataPath.toString(),
COMPILATION_REASON,
- RESULT_CODE);
+ RESULT_CODE,
+ ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE,
+ INSTRUCTION_SET);
// Assert
verifyWrites(ArtStatsLog.
- ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX);
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX);
} finally {
deleteSliently(dexMetadataPath);
deleteSliently(apk);
@@ -127,17 +132,18 @@
ArtStatsLogUtils.writeStatsLog(
mockLogger,
SESSION_ID,
- apk.toString(),
COMPILER_FILTER,
UID,
COMPILE_TIME,
dexMetadataPath.toString(),
COMPILATION_REASON,
- RESULT_CODE);
+ RESULT_CODE,
+ ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE,
+ INSTRUCTION_SET);
// Assert
verifyWrites(ArtStatsLog.
- ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE);
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE);
} finally {
deleteSliently(dexMetadataPath);
deleteSliently(apk);
@@ -157,17 +163,18 @@
ArtStatsLogUtils.writeStatsLog(
mockLogger,
SESSION_ID,
- apk.toString(),
COMPILER_FILTER,
UID,
COMPILE_TIME,
dexMetadataPath.toString(),
COMPILATION_REASON,
- RESULT_CODE);
+ RESULT_CODE,
+ ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE,
+ INSTRUCTION_SET);
// Assert
verifyWrites(ArtStatsLog.
- ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX);
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX);
} finally {
deleteSliently(dexMetadataPath);
deleteSliently(apk);
@@ -185,17 +192,18 @@
ArtStatsLogUtils.writeStatsLog(
mockLogger,
SESSION_ID,
- apk.toString(),
COMPILER_FILTER,
UID,
COMPILE_TIME,
/*dexMetadataPath=*/ null,
COMPILATION_REASON,
- RESULT_CODE);
+ RESULT_CODE,
+ ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE,
+ INSTRUCTION_SET);
// Assert
verifyWrites(ArtStatsLog.
- ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE);
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE);
} finally {
deleteSliently(apk);
}
@@ -214,23 +222,35 @@
ArtStatsLogUtils.writeStatsLog(
mockLogger,
SESSION_ID,
- apk.toString(),
COMPILER_FILTER,
UID,
COMPILE_TIME,
dexMetadataPath.toString(),
COMPILATION_REASON,
- RESULT_CODE);
+ RESULT_CODE,
+ ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE,
+ INSTRUCTION_SET);
// Assert
verifyWrites(ArtStatsLog.
- ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN);
+ ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN);
} finally {
deleteSliently(dexMetadataPath);
deleteSliently(apk);
}
}
+ @Test
+ public void testGetApkType() {
+ // Act
+ int result1 = ArtStatsLogUtils.getApkType(BASE_APK);
+ int result2 = ArtStatsLogUtils.getApkType(SPLIT_APK);
+
+ // Assert
+ Assert.assertEquals(result1, ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE);
+ Assert.assertEquals(result2, ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_SPLIT);
+ }
+
private void verifyWrites(int dexMetadataType) {
InOrder inorder = inOrder(mockLogger);
inorder.verify(mockLogger).write(
@@ -239,7 +259,9 @@
COMPILER_FILTER,
ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE,
RESULT_CODE,
- dexMetadataType);
+ dexMetadataType,
+ ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE,
+ INSTRUCTION_SET);
inorder.verify(mockLogger).write(
SESSION_ID,
UID,
@@ -247,7 +269,9 @@
COMPILER_FILTER,
ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
COMPILE_TIME,
- dexMetadataType);
+ dexMetadataType,
+ ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE,
+ INSTRUCTION_SET);
}
private Path zipFiles(String suffix, Path... files) throws IOException {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index a1b2f38..f9b8f26 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -55,8 +55,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.file.Files;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.util.Collection;
import java.util.Map;
import java.util.zip.ZipEntry;
@@ -413,7 +413,7 @@
}
final ApkLite baseApk = result.getResult();
final PackageLite pkgLite = new PackageLite(null, baseApk.getPath(), baseApk, null,
- null, null, null, null, null);
+ null, null, null, null, null, baseApk.getTargetSdkVersion());
Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite));
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 6e5fbd0..874f8dc 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -71,6 +71,7 @@
import android.service.dreams.DreamManagerInternal;
import android.test.mock.MockContentResolver;
import android.view.Display;
+import android.view.DisplayInfo;
import androidx.test.InstrumentationRegistry;
@@ -466,21 +467,21 @@
// First, ensure that a normal full wake lock does not cause a wakeup
int flags = PowerManager.FULL_WAKE_LOCK;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
// Ensure that the flag does *NOT* work with a partial wake lock.
flags = PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
// Verify that flag forces a wakeup when paired to a FULL_WAKE_LOCK
flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */);
}
@@ -594,6 +595,8 @@
public void testWasDeviceIdleFor_true() {
int interval = 1000;
createService();
+ mService.systemReady(null);
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
mService.onUserActivity();
advanceTime(interval + 1 /* just a little more */);
assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue();
@@ -603,6 +606,8 @@
public void testWasDeviceIdleFor_false() {
int interval = 1000;
createService();
+ mService.systemReady(null);
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
mService.onUserActivity();
assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
}
@@ -664,7 +669,7 @@
// Create a wakelock
mService.getBinderServiceInstance().acquireWakeLock(new Binder(), flags, tag, pkg,
- null /* workSource */, null /* historyTag */);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
assertThat(wakelockMap.get(tag)).isEqualTo(flags); // Verify wakelock is active.
// Confirm that the wakelocks have been disabled when the forceSuspend is in flight.
@@ -722,7 +727,7 @@
// Take a nap and verify we no longer hold the blocker
int flags = PowerManager.DOZE_WAKE_LOCK;
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
- null /* workSource */, null /* historyTag */);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
mService.getBinderServiceInstance().goToSleep(mClock.now(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
@@ -757,6 +762,9 @@
@Test
public void testInattentiveSleep_userActivityDismissesWarning() throws Exception {
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+ when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
setMinimumScreenOffTimeoutConfig(5);
setAttentiveWarningDuration(1900);
setAttentiveTimeout(2000);
@@ -827,13 +835,94 @@
mService.getBinderServiceInstance().acquireWakeLock(token,
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
- null /* workSource */, null /* historyTag */);
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
advanceTime(60);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
}
@Test
+ public void testWakeLock_affectsProperDisplayGroup() throws Exception {
+ final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+ new AtomicReference<>();
+ doAnswer((Answer<Void>) invocation -> {
+ listener.set(invocation.getArgument(0));
+ return null;
+ }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+ when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
+
+ final String pkg = mContextSpy.getOpPackageName();
+ final Binder token = new Binder();
+ final String tag = "testWakeLock_affectsProperDisplayGroup";
+
+ setMinimumScreenOffTimeoutConfig(5);
+ createService();
+ startSystem();
+ listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+ mService.getBinderServiceInstance().acquireWakeLock(token,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
+ null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY);
+
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+
+ advanceTime(15000);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
+ WAKEFULNESS_DOZING);
+ }
+
+ @Test
+ public void testInvalidDisplayGroupWakeLock_affectsAllDisplayGroups() throws Exception {
+ final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+ new AtomicReference<>();
+ doAnswer((Answer<Void>) invocation -> {
+ listener.set(invocation.getArgument(0));
+ return null;
+ }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+ when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
+
+ final String pkg = mContextSpy.getOpPackageName();
+ final Binder token = new Binder();
+ final String tag = "testInvalidDisplayGroupWakeLock_affectsAllDisplayGroups";
+
+ setMinimumScreenOffTimeoutConfig(5);
+ createService();
+ startSystem();
+ listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+ mService.getBinderServiceInstance().acquireWakeLock(token,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
+ null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY);
+
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+
+ advanceTime(15000);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ }
+
+ @Test
public void testBoot_ShouldBeAwake() throws Exception {
createService();
startSystem();
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
index dcff479..c29593f 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import android.hardware.vibrator.IVibrator;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
import android.os.vibrator.PrebakedSegment;
@@ -30,6 +31,7 @@
import org.junit.Test;
import java.util.Arrays;
+import java.util.stream.IntStream;
/**
* Tests for {@link DeviceVibrationEffectAdapter}.
@@ -75,6 +77,60 @@
}
@Test
+ public void testStepAndRampSegments_withPwleCapability_convertsStepsToRamps() {
+ VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
+ /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)),
+ /* repeatIndex= */ 2);
+
+ VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0,
+ /* startFrequency= */ 175, /* endFrequency= */ 175, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
+ /* startFrequency= */ 150, /* endFrequency= */ 150, /* duration= */ 100),
+ new RampSegment(/* startAmplitude= */ 0.1f, /* endAmplitude= */ 0.8f,
+ /* startFrequency= */ 50, /* endFrequency= */ 200, /* duration= */ 50),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.1f,
+ /* startFrequency= */ 200, /* endFrequency= */ 50, /* duration= */ 20)),
+ /* repeatIndex= */ 2);
+
+ VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
+ IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(expected, mAdapter.apply(effect, info));
+ }
+
+ @Test
+ public void testStepAndRampSegments_withPwleCapabilityAndNoFrequency_keepsOriginalSteps() {
+ VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
+ new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1,
+ /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
+ /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)),
+ /* repeatIndex= */ 2);
+
+ VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
+ new StepSegment(/* amplitude= */ 0, /* frequency= */ 150, /* duration= */ 10),
+ new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 150, /* duration= */ 100),
+ new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10),
+ new RampSegment(/* startAmplitude= */ 0.1f, /* endAmplitude= */ 0.8f,
+ /* startFrequency= */ 50, /* endFrequency= */ 200, /* duration= */ 50),
+ new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.1f,
+ /* startFrequency= */ 200, /* endFrequency= */ 50, /* duration= */ 20)),
+ /* repeatIndex= */ 2);
+
+ VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
+ IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ assertEquals(expected, mAdapter.apply(effect, info));
+ }
+
+ @Test
public void testStepAndRampSegments_emptyMapping_returnsSameAmplitudesAndFrequencyZero() {
VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
@@ -123,8 +179,10 @@
assertEquals(expected, mAdapter.apply(effect, createVibratorInfo(TEST_FREQUENCY_MAPPING)));
}
- private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyMapping frequencyMapping) {
- return new VibratorInfo(/* id= */ 0, /* capabilities= */ 0, null, null,
- /* qFactor= */ Float.NaN, frequencyMapping);
+ private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyMapping frequencyMapping,
+ int... capabilities) {
+ int cap = IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0);
+ return new VibratorInfo(/* id= */ 0, cap, null, null, null, /* qFactor= */ Float.NaN,
+ frequencyMapping);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 1e3c344..1d715c8 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -20,8 +20,10 @@
import android.os.Handler;
import android.os.Looper;
import android.os.VibrationEffect;
+import android.os.VibratorInfo;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
@@ -38,11 +40,11 @@
* interactions.
*/
final class FakeVibratorControllerProvider {
-
private static final int EFFECT_DURATION = 20;
private final Map<Long, PrebakedSegment> mEnabledAlwaysOnEffects = new HashMap<>();
private final List<VibrationEffectSegment> mEffectSegments = new ArrayList<>();
+ private final List<Integer> mBraking = new ArrayList<>();
private final List<Float> mAmplitudes = new ArrayList<>();
private final Handler mHandler;
private final FakeNativeWrapper mNativeWrapper;
@@ -52,9 +54,13 @@
private int mCapabilities;
private int[] mSupportedEffects;
+ private int[] mSupportedBraking;
private int[] mSupportedPrimitives;
- private float mResonantFrequency;
- private float mQFactor;
+ private float mMinFrequency = Float.NaN;
+ private float mResonantFrequency = Float.NaN;
+ private float mFrequencyResolution = Float.NaN;
+ private float mQFactor = Float.NaN;
+ private float[] mMaxAmplitudes;
private final class FakeNativeWrapper extends VibratorController.NativeWrapper {
public int vibratorId;
@@ -74,11 +80,12 @@
}
@Override
- public void on(long milliseconds, long vibrationId) {
+ public long on(long milliseconds, long vibrationId) {
mEffectSegments.add(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
/* frequency= */ 0, (int) milliseconds));
applyLatency();
scheduleListener(milliseconds, vibrationId);
+ return milliseconds;
}
@Override
@@ -92,26 +99,6 @@
}
@Override
- public int[] getSupportedEffects() {
- return mSupportedEffects;
- }
-
- @Override
- public int[] getSupportedPrimitives() {
- return mSupportedPrimitives;
- }
-
- @Override
- public float getResonantFrequency() {
- return mResonantFrequency;
- }
-
- @Override
- public float getQFactor() {
- return mQFactor;
- }
-
- @Override
public long perform(long effect, long strength, long vibrationId) {
if (mSupportedEffects == null
|| Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) {
@@ -136,12 +123,20 @@
}
@Override
- public void setExternalControl(boolean enabled) {
+ public long composePwle(RampSegment[] primitives, int braking, long vibrationId) {
+ long duration = 0;
+ for (RampSegment primitive : primitives) {
+ duration += primitive.getDuration();
+ mEffectSegments.add(primitive);
+ }
+ mBraking.add(braking);
+ applyLatency();
+ scheduleListener(duration, vibrationId);
+ return duration;
}
@Override
- public long getCapabilities() {
- return mCapabilities;
+ public void setExternalControl(boolean enabled) {
}
@Override
@@ -155,6 +150,15 @@
mEnabledAlwaysOnEffects.remove(id);
}
+ @Override
+ public VibratorInfo getInfo(float suggestedFrequencyRange) {
+ VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
+ mMinFrequency, mResonantFrequency, mFrequencyResolution,
+ suggestedFrequencyRange, mMaxAmplitudes);
+ return new VibratorInfo(vibratorId, mCapabilities, mSupportedEffects, mSupportedBraking,
+ mSupportedPrimitives, mQFactor, frequencyMapping);
+ }
+
private void applyLatency() {
try {
if (mLatency > 0) {
@@ -214,6 +218,15 @@
mSupportedEffects = effects;
}
+ /** Set the effects supported by the fake vibrator hardware. */
+ public void setSupportedBraking(int... braking) {
+ if (braking != null) {
+ braking = Arrays.copyOf(braking, braking.length);
+ Arrays.sort(braking);
+ }
+ mSupportedBraking = braking;
+ }
+
/** Set the primitives supported by the fake vibrator hardware. */
public void setSupportedPrimitives(int... primitives) {
if (primitives != null) {
@@ -224,8 +237,18 @@
}
/** Set the resonant frequency of the fake vibrator hardware. */
- public void setResonantFrequency(float resonantFrequency) {
- mResonantFrequency = resonantFrequency;
+ public void setResonantFrequency(float frequencyHz) {
+ mResonantFrequency = frequencyHz;
+ }
+
+ /** Set the minimum frequency of the fake vibrator hardware. */
+ public void setMinFrequency(float frequencyHz) {
+ mMinFrequency = frequencyHz;
+ }
+
+ /** Set the frequency resolution of the fake vibrator hardware. */
+ public void setFrequencyResolution(float frequencyHz) {
+ mFrequencyResolution = frequencyHz;
}
/** Set the Q factor of the fake vibrator hardware. */
@@ -233,6 +256,11 @@
mQFactor = qFactor;
}
+ /** Set the max amplitude supported for each frequency f the fake vibrator hardware. */
+ public void setMaxAmplitudes(float... maxAmplitudes) {
+ mMaxAmplitudes = maxAmplitudes;
+ }
+
/**
* Return the amplitudes set by this controller, including zeroes for each time the vibrator was
* turned off.
@@ -241,6 +269,11 @@
return new ArrayList<>(mAmplitudes);
}
+ /** Return the braking values passed to the compose PWLE method. */
+ public List<Integer> getBraking() {
+ return mBraking;
+ }
+
/** Return list of {@link VibrationEffectSegment} played by this controller, in order. */
public List<VibrationEffectSegment> getEffectSegments() {
return new ArrayList<>(mEffectSegments);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 37e0ec2..c439b9c 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -27,9 +27,11 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
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.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.IVibratorManager;
import android.os.CombinedVibrationEffect;
@@ -42,6 +44,7 @@
import android.os.test.TestLooper;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.annotations.LargeTest;
@@ -382,6 +385,78 @@
}
@Test
+ public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() throws Exception {
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_TICK);
+ mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
+ IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ long vibrationId = 1;
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addEffect(VibrationEffect.createOneShot(10, 100))
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK), 10)
+ .compose();
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+ waitForCompletion(thread);
+
+ // Use first duration the vibrator is turned on since we cannot estimate the clicks.
+ verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
+ verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+ assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertEquals(Arrays.asList(
+ expectedOneShot(10),
+ expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
+ expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0),
+ expectedPrebaked(VibrationEffect.EFFECT_CLICK),
+ expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
+ mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
+ assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
+ }
+
+ @Test
+ public void vibrate_singleVibratorPwle_runsComposePwle() throws Exception {
+ mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedBraking(Braking.CLAB);
+ mVibratorProviders.get(VIBRATOR_ID).setMinFrequency(100);
+ mVibratorProviders.get(VIBRATOR_ID).setResonantFrequency(150);
+ mVibratorProviders.get(VIBRATOR_ID).setFrequencyResolution(50);
+ mVibratorProviders.get(VIBRATOR_ID).setMaxAmplitudes(
+ 0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
+
+ long vibrationId = 1;
+ VibrationEffect effect = VibrationEffect.startWaveform()
+ .addStep(1, 10)
+ .addRamp(0, 20)
+ .addStep(0.8f, 1, 30)
+ .addRamp(0.6f, -1, 40)
+ .build();
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+ waitForCompletion(thread);
+
+ verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(100L));
+ verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+ assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
+ assertEquals(Arrays.asList(
+ expectedRamp(/* amplitude= */ 1, /* frequency= */ 150, /* duration= */ 10),
+ expectedRamp(/* StartAmplitude= */ 1, /* endAmplitude= */ 0,
+ /* startFrequency= */ 150, /* endFrequency= */ 150, /* duration= */ 20),
+ expectedRamp(/* amplitude= */ 0.6f, /* frequency= */ 200, /* duration= */ 30),
+ expectedRamp(/* StartAmplitude= */ 0.6f, /* endAmplitude= */ 0.5f,
+ /* startFrequency= */ 200, /* endFrequency= */ 100, /* duration= */ 40)),
+ mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
+ assertEquals(Arrays.asList(Braking.CLAB), mVibratorProviders.get(VIBRATOR_ID).getBraking());
+ }
+
+ @Test
public void vibrate_singleVibratorCancelled_vibratorStopped() throws Exception {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
@@ -932,6 +1007,16 @@
return new PrimitiveSegment(primitiveId, scale, delay);
}
+ private VibrationEffectSegment expectedRamp(float amplitude, float frequency, int duration) {
+ return expectedRamp(amplitude, amplitude, frequency, frequency, duration);
+ }
+
+ private VibrationEffectSegment expectedRamp(float startAmplitude, float endAmplitude,
+ float startFrequency, float endFrequency, int duration) {
+ return new RampSegment(startAmplitude, endAmplitude, startFrequency, endFrequency,
+ duration);
+ }
+
private List<Float> expectedAmplitudes(int... amplitudes) {
return Arrays.stream(amplitudes)
.mapToObj(amplitude -> amplitude / 255f)
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
index 70ea219..2e9aad1 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -21,6 +21,7 @@
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.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
@@ -34,10 +35,12 @@
import android.content.ContentResolver;
import android.content.ContextWrapper;
+import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
import android.os.IBinder;
import android.os.IVibratorStateListener;
import android.os.VibrationEffect;
+import android.os.VibratorInfo;
import android.os.test.TestLooper;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
@@ -66,6 +69,7 @@
*/
@Presubmit
public class VibratorControllerTest {
+ private static final int VIBRATOR_ID = 0;
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@@ -86,23 +90,18 @@
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock);
+ mockVibratorCapabilities(0);
}
private VibratorController createController() {
- return new VibratorController(/* vibratorId= */ 0, mOnCompleteListenerMock,
- mNativeWrapperMock);
- }
-
- private VibratorController createController(int vibratorId) {
- return new VibratorController(vibratorId, mOnCompleteListenerMock, mNativeWrapperMock);
+ return new VibratorController(VIBRATOR_ID, mOnCompleteListenerMock, mNativeWrapperMock);
}
@Test
public void createController_initializesNativeWrapper() {
- int vibratorId = 13;
- VibratorController controller = createController(vibratorId);
- assertEquals(vibratorId, controller.getVibratorInfo().getId());
- verify(mNativeWrapperMock).init(eq(vibratorId), notNull());
+ VibratorController controller = createController();
+ assertEquals(VIBRATOR_ID, controller.getVibratorInfo().getId());
+ verify(mNativeWrapperMock).init(eq(VIBRATOR_ID), notNull());
}
@Test
@@ -191,6 +190,7 @@
@Test
public void on_withDuration_turnsVibratorOn() {
+ when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
controller.on(100, 10);
@@ -228,20 +228,26 @@
}
@Test
- public void on_withComposedPwle_ignoresEffect() {
+ public void on_withComposedPwle_performsEffect() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ when(mNativeWrapperMock.composePwle(any(), anyInt(), anyLong())).thenReturn(15L);
VibratorController controller = createController();
RampSegment[] primitives = new RampSegment[]{
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
/* startFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 10)
};
- assertEquals(0L, controller.on(primitives, 12));
- assertFalse(controller.isVibrating());
+ assertEquals(15L, controller.on(primitives, 12));
+ assertTrue(controller.isVibrating());
+
+ verify(mNativeWrapperMock).composePwle(eq(primitives), eq(Braking.NONE), eq(12L));
}
@Test
public void off_turnsOffVibrator() {
+ when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
+
controller.on(100, 1);
assertTrue(controller.isVibrating());
@@ -253,6 +259,7 @@
@Test
public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
+ when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
controller.registerVibratorStateListener(mVibratorStateListenerMock);
@@ -271,6 +278,7 @@
@Test
public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception {
+ when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
controller.registerVibratorStateListener(mVibratorStateListenerMock);
@@ -287,7 +295,11 @@
}
private void mockVibratorCapabilities(int capabilities) {
- when(mNativeWrapperMock.getCapabilities()).thenReturn((long) capabilities);
+ VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
+ Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
+ when(mNativeWrapperMock.getInfo(/* suggestedFrequencyRange= */ 100)).thenReturn(
+ new VibratorInfo(VIBRATOR_ID, capabilities, null, null, null, Float.NaN,
+ frequencyMapping));
}
private PrebakedSegment createPrebaked(int effectId, int effectStrength) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
index f5d0ca7..584bcf4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -58,7 +58,6 @@
private com.android.server.wm.WindowOrientationListener mWindowOrientationListener;
private int mFinalizedRotation;
private boolean mRotationResolverEnabled;
- private boolean mCanUseRotationResolver;
private SensorEvent mFakeSensorEvent;
private Sensor mFakeSensor;
@@ -66,7 +65,6 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mRotationResolverEnabled = true;
- mCanUseRotationResolver = true;
mFakeRotationResolverInternal = new TestableRotationResolver();
doReturn(mMockSensorManager).when(mMockContext).getSystemService(Context.SENSOR_SERVICE);
@@ -89,16 +87,6 @@
}
@Test
- public void testOnSensorChanged_cannotUseRotationResolver_useSensorResult() {
- mCanUseRotationResolver = false;
-
- mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
-
- assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
-
- }
-
- @Test
public void testOnSensorChanged_normalCase() {
mFakeRotationResolverInternal.mResult = Surface.ROTATION_180;
@@ -139,11 +127,6 @@
}
@Override
- public boolean canUseRotationResolver() {
- return mCanUseRotationResolver;
- }
-
- @Override
public boolean isRotationResolverEnabled() {
return mRotationResolverEnabled;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index be6e801..27a4826 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -58,7 +58,6 @@
import android.util.TypedXmlSerializer;
import android.util.Xml;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import com.android.server.UiServiceTestCase;
@@ -70,8 +69,6 @@
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -516,6 +513,20 @@
assertTrue(components.contains(new ComponentName("package", "default")));
}
+ @Test
+ public void resetPackage_clearsUserSet() {
+ // setup
+ ManagedServices service =
+ new TestManagedServices(
+ getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT);
+ String componentName = "package/user-allowed";
+ service.addApprovedList(componentName, 0, true);
+
+ service.resetComponents("package", 0);
+
+ assertFalse(service.isPackageOrComponentUserSet(componentName, 0));
+ }
+
/** Test that backup only writes packages/components that belong to the target user. */
@Test
public void testWriteXml_onlyBackupsForTargetUser() throws Exception {
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 55ebe11..bb70c79 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6972,51 +6972,6 @@
}
@Test
- public void deleteConversationNotificationChannels() throws Exception {
- NotificationChannel messagesParent =
- new NotificationChannel("messages", "messages", IMPORTANCE_HIGH);
- Parcel msgParcel = Parcel.obtain();
- messagesParent.writeToParcel(msgParcel, 0);
- msgParcel.setDataPosition(0);
-
- NotificationChannel callsParent =
- new NotificationChannel("calls", "calls", IMPORTANCE_HIGH);
- Parcel callParcel = Parcel.obtain();
- callsParent.writeToParcel(callParcel, 0);
- callParcel.setDataPosition(0);
-
- mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList(
- messagesParent, callsParent)));
-
- String conversationId = "friend";
-
- mBinderService.createConversationNotificationChannelForPackage(
- PKG, mUid, NotificationChannel.CREATOR.createFromParcel(msgParcel),
- conversationId);
- mBinderService.createConversationNotificationChannelForPackage(
- PKG, mUid, NotificationChannel.CREATOR.createFromParcel(callParcel),
- conversationId);
-
- NotificationChannel messagesChild = mBinderService.getConversationNotificationChannel(
- PKG, 0, PKG, messagesParent.getId(), false, conversationId);
- NotificationChannel callsChild = mBinderService.getConversationNotificationChannel(
- PKG, 0, PKG, callsParent.getId(), false, conversationId);
-
- assertEquals(messagesParent.getId(), messagesChild.getParentChannelId());
- assertEquals(conversationId, messagesChild.getConversationId());
-
- assertEquals(callsParent.getId(), callsChild.getParentChannelId());
- assertEquals(conversationId, callsChild.getConversationId());
-
- mBinderService.deleteConversationNotificationChannels(PKG, mUid, conversationId);
-
- assertNull(mBinderService.getConversationNotificationChannel(
- PKG, 0, PKG, messagesParent.getId(), false, conversationId));
- assertNull(mBinderService.getConversationNotificationChannel(
- PKG, 0, PKG, callsParent.getId(), false, conversationId));
- }
-
- @Test
public void testCorrectCategory_systemOn_appCannotTurnOff() {
int requested = 0;
int system = PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index acda4d5..04c144a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -71,6 +71,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.content.AttributionSource;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -86,6 +87,7 @@
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -207,37 +209,35 @@
throw new UnsupportedOperationException("unimplemented mock method");
});
doAnswer(invocation -> {
- String callingPkg = invocation.getArgument(0);
- String featureId = invocation.getArgument(1);
- Uri uri = invocation.getArgument(2);
- RemoteCallback cb = invocation.getArgument(3);
+ AttributionSource attributionSource = invocation.getArgument(0);
+ Uri uri = invocation.getArgument(1);
+ RemoteCallback cb = invocation.getArgument(2);
IContentProvider mock = (IContentProvider) (invocation.getMock());
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
final Bundle bundle = new Bundle();
try {
bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- mock.canonicalize(callingPkg, featureId, uri));
+ mock.canonicalize(attributionSource, uri));
} catch (RemoteException e) { /* consume */ }
cb.sendResult(bundle);
});
return null;
- }).when(mTestIContentProvider).canonicalizeAsync(any(), any(), any(), any());
+ }).when(mTestIContentProvider).canonicalizeAsync(any(), any(), any());
doAnswer(invocation -> {
- String callingPkg = invocation.getArgument(0);
- String featureId = invocation.getArgument(1);
- Uri uri = invocation.getArgument(2);
- RemoteCallback cb = invocation.getArgument(3);
+ AttributionSource attributionSource = invocation.getArgument(0);
+ Uri uri = invocation.getArgument(1);
+ RemoteCallback cb = invocation.getArgument(2);
IContentProvider mock = (IContentProvider) (invocation.getMock());
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
final Bundle bundle = new Bundle();
try {
bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- mock.uncanonicalize(callingPkg, featureId, uri));
+ mock.uncanonicalize(attributionSource, uri));
} catch (RemoteException e) { /* consume */ }
cb.sendResult(bundle);
});
return null;
- }).when(mTestIContentProvider).uncanonicalizeAsync(any(), any(), any(), any());
+ }).when(mTestIContentProvider).uncanonicalizeAsync(any(), any(), any());
doAnswer(invocation -> {
Uri uri = invocation.getArgument(0);
RemoteCallback cb = invocation.getArgument(1);
@@ -256,11 +256,11 @@
contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
doReturn(CANONICAL_SOUND_URI)
- .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI));
+ .when(mTestIContentProvider).canonicalize(any(), eq(SOUND_URI));
doReturn(CANONICAL_SOUND_URI)
- .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI));
doReturn(SOUND_URI)
- .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
@@ -594,7 +594,7 @@
// Testing that in restore we are given the canonical version
loadStreamXml(baos, true, UserHandle.USER_SYSTEM);
- verify(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
}
@Test
@@ -605,12 +605,11 @@
.appendQueryParameter("canonical", "1")
.build();
doReturn(canonicalBasedOnLocal)
- .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI));
doReturn(localUri)
- .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
doReturn(localUri)
- .when(mTestIContentProvider).uncanonicalize(any(), any(),
- eq(canonicalBasedOnLocal));
+ .when(mTestIContentProvider).uncanonicalize(any(), eq(canonicalBasedOnLocal));
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -630,9 +629,9 @@
public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
Thread.sleep(3000);
doReturn(null)
- .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI));
doReturn(null)
- .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ .when(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -657,7 +656,7 @@
public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
// Not a local uncanonicalized uri, simulating that it fails to exist locally
doReturn(null)
- .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI));
+ .when(mTestIContentProvider).canonicalize(any(), eq(SOUND_URI));
String id = "id";
String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 5018166..b83f9f2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -132,11 +132,11 @@
when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
- when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(SOUND_URI);
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index aceed86..baae25c 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -110,7 +110,7 @@
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
TestableLooper.get(this).processAllMessages();
- verify(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
+ verify(mIContentProvider).call(any(), anyString(),
eq(SliceProvider.METHOD_PIN), eq(null), argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
@@ -168,8 +168,8 @@
// Throw exception when trying to pin
doAnswer(invocation -> {
throw new Exception("Pin failed");
- }).when(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
- anyString(), eq(null), any());
+ }).when(mIContentProvider).call(any(), anyString(), anyString(),
+ nullable(String.class), any());
TestableLooper.get(this).processAllMessages();
@@ -177,7 +177,7 @@
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
TestableLooper.get(this).processAllMessages();
- verify(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
+ verify(mIContentProvider).call(any(), anyString(),
eq(SliceProvider.METHOD_PIN), eq(null), argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 3025a95..222c692 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -26,6 +26,7 @@
import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
@@ -62,6 +63,10 @@
private long mLongPressTime;
private long mVeryLongPressTime;
+ // Allow press from non interactive mode.
+ private boolean mAllowNonInteractiveForPress = true;
+ private boolean mAllowNonInteractiveForLongPress = true;
+
@Before
public void setUp() {
mDetector = new SingleKeyGestureDetector(mContext);
@@ -81,11 +86,17 @@
}
@Override
public void onPress(long downTime) {
+ if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
+ return;
+ }
mShortPressed.countDown();
}
@Override
void onLongPress(long downTime) {
+ if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForLongPress) {
+ return;
+ }
mLongPressed.countDown();
}
@@ -96,6 +107,9 @@
@Override
void onMultiPress(long downTime, int count) {
+ if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
+ return;
+ }
mMultiPressed.countDown();
assertEquals(mMaxMultiPressPowerCount, count);
}
@@ -103,9 +117,13 @@
}
private void pressKey(long eventTime, int keyCode, long pressTime) {
+ pressKey(eventTime, keyCode, pressTime, true /* interactive */);
+ }
+
+ private void pressKey(long eventTime, int keyCode, long pressTime, boolean interactive) {
final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN,
keyCode, 0 /* repeat */, 0 /* metaState */);
- mDetector.interceptKey(keyDown);
+ mDetector.interceptKey(keyDown, interactive);
// keep press down.
try {
@@ -118,7 +136,7 @@
final KeyEvent keyUp = new KeyEvent(eventTime, eventTime, ACTION_UP,
keyCode, 0 /* repeat */, 0 /* metaState */);
- mDetector.interceptKey(keyUp);
+ mDetector.interceptKey(keyUp, interactive);
}
@Test
@@ -149,4 +167,18 @@
pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
}
+
+ @Test
+ public void testNonInteractive() throws InterruptedException {
+ long eventTime = SystemClock.uptimeMillis();
+ // Disallow short press behavior from non interactive.
+ mAllowNonInteractiveForPress = false;
+ pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */, false /* interactive */);
+ assertFalse(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+
+ // Allow long press behavior from non interactive.
+ eventTime = SystemClock.uptimeMillis();
+ pressKey(eventTime, KEYCODE_POWER, mLongPressTime, false /* interactive */);
+ assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
deleted file mode 100644
index 1700707..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-
-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.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link DisplayContent} class.
- *
- * Build/Install/Run:
- * atest WmTests:ActivityDisplayTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-// TODO(b/144248496): Merge to DisplayContentTests
-public class ActivityDisplayTests extends WindowTestsBase {
-
- @Test
- public void testLastFocusedStackIsUpdatedWhenMovingStack() {
- // Create a stack at bottom.
- final TaskDisplayArea taskDisplayAreas =
- mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea();
- final Task stack =
- new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
- final Task prevFocusedStack = taskDisplayAreas.getFocusedRootTask();
-
- stack.moveToFront("moveStackToFront");
- // After moving the stack to front, the previous focused should be the last focused.
- assertTrue(stack.isFocusedRootTaskOnDisplay());
- assertEquals(prevFocusedStack, taskDisplayAreas.getLastFocusedRootTask());
-
- stack.moveToBack("moveStackToBack", null /* task */);
- // After moving the stack to back, the stack should be the last focused.
- assertEquals(stack, taskDisplayAreas.getLastFocusedRootTask());
- }
-
- /**
- * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
- * stack. The fullscreen stack should be the top focused for resuming correctly.
- */
- @Test
- public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
- // Create a pinned stack and move to front.
- final Task pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea()
- .createRootTask(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
- final Task pinnedTask = new TaskBuilder(mAtm.mTaskSupervisor)
- .setParentTask(pinnedStack).build();
- new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
- .setTask(pinnedTask).build();
- pinnedStack.moveToFront("movePinnedStackToFront");
-
- // The focused stack should be the pinned stack.
- assertTrue(pinnedStack.isFocusedRootTaskOnDisplay());
-
- // Create a fullscreen stack and move to front.
- final Task fullscreenStack = createFullscreenStackWithSimpleActivityAt(
- mRootWindowContainer.getDefaultDisplay());
- fullscreenStack.moveToFront("moveFullscreenStackToFront");
-
- // The focused stack should be the fullscreen stack.
- assertTrue(fullscreenStack.isFocusedRootTaskOnDisplay());
- }
-
- /**
- * Test {@link TaskDisplayArea#mPreferredTopFocusableRootTask} will be cleared when
- * the stack is removed or moved to back, and the focused stack will be according to z-order.
- */
- @Test
- public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
- // Create a display which only contains 2 stacks.
- final DisplayContent display = addNewDisplayContentAt(POSITION_TOP);
- final Task stack1 = createFullscreenStackWithSimpleActivityAt(display);
- final Task stack2 = createFullscreenStackWithSimpleActivityAt(display);
-
- // Put stack1 and stack2 on top.
- stack1.moveToFront("moveStack1ToFront");
- stack2.moveToFront("moveStack2ToFront");
- assertTrue(stack2.isFocusedRootTaskOnDisplay());
-
- // Stack1 should be focused after moving stack2 to back.
- stack2.moveToBack("moveStack2ToBack", null /* task */);
- assertTrue(stack1.isFocusedRootTaskOnDisplay());
-
- // Stack2 should be focused after removing stack1.
- stack1.getDisplayArea().removeRootTask(stack1);
- assertTrue(stack2.isFocusedRootTaskOnDisplay());
- }
-
- /**
- * Verifies {@link DisplayContent#remove} should not resume home stack on the removing display.
- */
- @Test
- public void testNotResumeHomeStackOnRemovingDisplay() {
- // Create a display which supports system decoration and allows reparenting stacks to
- // another display when the display is removed.
- final DisplayContent display = new TestDisplayContent.Builder(
- mAtm, 1000, 1500).setSystemDecorations(true).build();
- doReturn(false).when(display).shouldDestroyContentOnRemove();
-
- // Put home stack on the display.
- final Task homeStack = new TaskBuilder(mSupervisor)
- .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build();
-
- // Put a finishing standard activity which will be reparented.
- final Task stack = createFullscreenStackWithSimpleActivityAt(display);
- stack.topRunningActivity().makeFinishingLocked();
-
- clearInvocations(homeStack);
- display.remove();
-
- // The removed display should have no focused stack and its home stack should never resume.
- assertNull(display.getFocusedRootTask());
- verify(homeStack, never()).resumeTopActivityUncheckedLocked(any(), any());
- }
-
- private Task createFullscreenStackWithSimpleActivityAt(DisplayContent display) {
- final Task fullscreenStack = display.getDefaultTaskDisplayArea().createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
- final Task fullscreenTask = new TaskBuilder(mAtm.mTaskSupervisor)
- .setParentTask(fullscreenStack).build();
- new ActivityBuilder(mAtm).setTask(fullscreenTask).build();
- return fullscreenStack;
- }
-
- /**
- * Verifies the correct activity is returned when querying the top running activity.
- */
- @Test
- public void testTopRunningActivity() {
- final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
- final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
- final ActivityRecord activity = stack.getTopNonFinishingActivity();
-
- // Create empty stack on top.
- final Task emptyStack = new TaskBuilder(mSupervisor).build();
-
- // Make sure the top running activity is not affected when keyguard is not locked.
- assertTopRunningActivity(activity, display);
-
- // Check to make sure activity not reported when it cannot show on lock and lock is on.
- doReturn(true).when(keyguard).isKeyguardLocked();
- assertEquals(activity, display.topRunningActivity());
- assertNull(display.topRunningActivity(true /* considerKeyguardState */));
-
- // Move stack with activity to top.
- stack.moveToFront("testStackToFront");
- assertEquals(stack, display.getFocusedRootTask());
- assertEquals(activity, display.topRunningActivity());
- assertNull(display.topRunningActivity(true /* considerKeyguardState */));
-
- // Add activity that should be shown on the keyguard.
- final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mAtm)
- .setTask(stack)
- .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
- .build();
-
- // Ensure the show when locked activity is returned.
- assertTopRunningActivity(showWhenLockedActivity, display);
-
- // Move empty stack to front. The running activity in focusable stack which below the
- // empty stack should be returned.
- emptyStack.moveToFront("emptyStackToFront");
- assertEquals(stack, display.getFocusedRootTask());
- assertTopRunningActivity(showWhenLockedActivity, display);
- }
-
- private static void assertTopRunningActivity(ActivityRecord top, DisplayContent display) {
- assertEquals(top, display.topRunningActivity());
- assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
- }
-
- /**
- * This test enforces that alwaysOnTop stack is placed at proper position.
- */
- @Test
- public void testAlwaysOnTopStackLocation() {
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final Task alwaysOnTopStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
- ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(alwaysOnTopStack).build();
- alwaysOnTopStack.setAlwaysOnTop(true);
- taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopStack,
- false /* includingParents */);
- assertTrue(alwaysOnTopStack.isAlwaysOnTop());
- // Ensure always on top state is synced to the children of the stack.
- assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
- assertEquals(alwaysOnTopStack, taskDisplayArea.getTopRootTask());
-
- final Task pinnedStack = taskDisplayArea.createRootTask(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- assertEquals(pinnedStack, taskDisplayArea.getRootPinnedTask());
- assertEquals(pinnedStack, taskDisplayArea.getTopRootTask());
-
- final Task anotherAlwaysOnTopStack = taskDisplayArea.createRootTask(
- WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- anotherAlwaysOnTopStack.setAlwaysOnTop(true);
- taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
- false /* includingParents */);
- assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
- int topPosition = taskDisplayArea.getRootTaskCount() - 1;
- // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
- // existing alwaysOnTop stack.
- assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
-
- final Task nonAlwaysOnTopStack = taskDisplayArea.createRootTask(
- WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- assertEquals(taskDisplayArea, nonAlwaysOnTopStack.getDisplayArea());
- topPosition = taskDisplayArea.getRootTaskCount() - 1;
- // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
- // existing other non-alwaysOnTop stacks.
- assertEquals(topPosition - 3, getTaskIndexOf(taskDisplayArea, nonAlwaysOnTopStack));
-
- anotherAlwaysOnTopStack.setAlwaysOnTop(false);
- taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
- false /* includingParents */);
- assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
- // Ensure, when always on top is turned off for a stack, the stack is put just below all
- // other always on top stacks.
- assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
- anotherAlwaysOnTopStack.setAlwaysOnTop(true);
-
- // Ensure always on top state changes properly when windowing mode changes.
- anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
- assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
- anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
- assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
-
- final Task dreamStack = taskDisplayArea.createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */);
- assertEquals(taskDisplayArea, dreamStack.getDisplayArea());
- assertTrue(dreamStack.isAlwaysOnTop());
- topPosition = taskDisplayArea.getRootTaskCount() - 1;
- // Ensure dream shows above all activities, including PiP
- assertEquals(dreamStack, taskDisplayArea.getTopRootTask());
- assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, pinnedStack));
-
- final Task assistStack = taskDisplayArea.createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
- assertEquals(taskDisplayArea, assistStack.getDisplayArea());
- assertFalse(assistStack.isAlwaysOnTop());
- topPosition = taskDisplayArea.getRootTaskCount() - 1;
-
- // Ensure Assistant shows as a non-always-on-top activity when config_assistantOnTopOfDream
- // is false and on top of everything when true.
- final boolean isAssistantOnTop = mContext.getResources()
- .getBoolean(com.android.internal.R.bool.config_assistantOnTopOfDream);
- assertEquals(isAssistantOnTop ? topPosition : topPosition - 4,
- getTaskIndexOf(taskDisplayArea, assistStack));
- }
-
- @Test
- public void testRemoveRootTaskInWindowingModes() {
- removeStackTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes(
- WINDOWING_MODE_FULLSCREEN));
- }
-
- @Test
- public void testRemoveStackWithActivityTypes() {
- removeStackTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes(
- ACTIVITY_TYPE_STANDARD));
- }
-
- private void removeStackTests(Runnable runnable) {
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final Task stack1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, ON_TOP);
- final Task stack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, ON_TOP);
- final Task stack3 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, ON_TOP);
- final Task stack4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, ON_TOP);
- final Task task1 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack1).build();
- final Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack2).build();
- final Task task3 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack3).build();
- final Task task4 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack4).build();
-
- // Reordering stacks while removing stacks.
- doAnswer(invocation -> {
- taskDisplayArea.positionChildAt(POSITION_TOP, stack3, false /*includingParents*/);
- return true;
- }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
-
- // Removing stacks from the display while removing stacks.
- doAnswer(invocation -> {
- taskDisplayArea.removeRootTask(stack2);
- return true;
- }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
-
- runnable.run();
- verify(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
- verify(mSupervisor).removeTask(eq(task3), anyBoolean(), anyBoolean(), any());
- verify(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
- verify(mSupervisor).removeTask(eq(task1), anyBoolean(), anyBoolean(), any());
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index ce5fc40..678defe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -405,7 +405,7 @@
public void testGetAnimationTargets_taskContainsMultipleTasks() {
// [DisplayContent] - [Task] -+- [Task1] - [ActivityRecord1] (opening, invisible)
// +- [Task2] - [ActivityRecord2] (closing, visible)
- final Task parentTask = createTaskStackOnDisplay(mDisplayContent);
+ final Task parentTask = createTask(mDisplayContent);
final ActivityRecord activity1 = createActivityRecordWithParentTask(parentTask);
activity1.setVisible(false);
activity1.mVisibleRequested = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 83aca5e..c3279bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -177,13 +177,13 @@
}
@Test
- public void testCleanAppTransitionWhenTaskStackReparent() {
+ public void testCleanAppTransitionWhenRootTaskReparent() {
// Create 2 displays & presume both display the state is ON for ready to display & animate.
final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
final DisplayContent dc2 = createNewDisplay(Display.STATE_ON);
- final Task stack1 = createTaskStackOnDisplay(dc1);
- final Task task1 = createTaskInStack(stack1, 0 /* userId */);
+ final Task rootTask1 = createTask(dc1);
+ final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */);
final ActivityRecord activity1 = createNonAttachedActivityRecord(dc1);
task1.addChild(activity1, 0);
@@ -198,8 +198,8 @@
dc1.mOpeningApps.add(activity1);
assertTrue(dc1.mOpeningApps.size() > 0);
- // Move stack to another display.
- stack1.reparent(dc2.getDefaultTaskDisplayArea(), true);
+ // Move root task to another display.
+ rootTask1.reparent(dc2.getDefaultTaskDisplayArea(), true);
// Verify if token are cleared from both pending transition list in former display.
assertFalse(dc1.mOpeningApps.contains(activity1));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 24b4f65..0afd39f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -16,10 +16,12 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -65,6 +67,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
@@ -350,29 +353,29 @@
}
/**
- * This tests stack movement between displays and proper stack's, task's and app token's display
- * container references updates.
+ * This tests root task movement between displays and proper root task's, task's and app token's
+ * display container references updates.
*/
@Test
- public void testMoveStackBetweenDisplays() {
+ public void testMoveRootTaskBetweenDisplays() {
// Create a second display.
final DisplayContent dc = createNewDisplay();
- // Add stack with activity.
- final Task stack = createTaskStackOnDisplay(dc);
- assertEquals(dc.getDisplayId(), stack.getDisplayContent().getDisplayId());
- assertEquals(dc, stack.getDisplayContent());
+ // Add root task with activity.
+ final Task rootTask = createTask(dc);
+ assertEquals(dc.getDisplayId(), rootTask.getDisplayContent().getDisplayId());
+ assertEquals(dc, rootTask.getDisplayContent());
- final Task task = createTaskInStack(stack, 0 /* userId */);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createNonAttachedActivityRecord(dc);
task.addChild(activity, 0);
assertEquals(dc, task.getDisplayContent());
assertEquals(dc, activity.getDisplayContent());
- // Move stack to first display.
- stack.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true /* onTop */);
- assertEquals(mDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId());
- assertEquals(mDisplayContent, stack.getDisplayContent());
+ // Move root task to first display.
+ rootTask.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true /* onTop */);
+ assertEquals(mDisplayContent.getDisplayId(), rootTask.getDisplayContent().getDisplayId());
+ assertEquals(mDisplayContent, rootTask.getDisplayContent());
assertEquals(mDisplayContent, task.getDisplayContent());
assertEquals(mDisplayContent, activity.getDisplayContent());
}
@@ -424,7 +427,7 @@
}
/**
- * Tests tapping on a stack in different display results in window gaining focus.
+ * Tests tapping on a root task in different display results in window gaining focus.
*/
@Test
public void testInputEventBringsCorrectDisplayInFocus() {
@@ -432,16 +435,16 @@
// Create a second display
final DisplayContent dc1 = createNewDisplay();
- // Add stack with activity.
- final Task stack0 = createTaskStackOnDisplay(dc0);
- final Task task0 = createTaskInStack(stack0, 0 /* userId */);
+ // Add root task with activity.
+ final Task rootTask0 = createTask(dc0);
+ final Task task0 = createTaskInRootTask(rootTask0, 0 /* userId */);
final ActivityRecord activity = createNonAttachedActivityRecord(dc0);
task0.addChild(activity, 0);
dc0.configureDisplayPolicy();
assertNotNull(dc0.mTapDetector);
- final Task stack1 = createTaskStackOnDisplay(dc1);
- final Task task1 = createTaskInStack(stack1, 0 /* userId */);
+ final Task rootTask1 = createTask(dc1);
+ final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */);
final ActivityRecord activity1 = createNonAttachedActivityRecord(dc0);
task1.addChild(activity1, 0);
dc1.configureDisplayPolicy();
@@ -889,13 +892,13 @@
final DisplayContent newDisplay = createNewDisplay();
final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
- final Task stack = mDisplayContent.getTopRootTask();
- final ActivityRecord activity = stack.topRunningActivity();
+ final Task rootTask = mDisplayContent.getTopRootTask();
+ final ActivityRecord activity = rootTask.topRunningActivity();
doReturn(true).when(activity).shouldBeVisibleUnchecked();
final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1");
- final Task stack1 = newDisplay.getTopRootTask();
- final ActivityRecord activity1 = stack1.topRunningActivity();
+ final Task rootTask1 = newDisplay.getTopRootTask();
+ final ActivityRecord activity1 = rootTask1.topRunningActivity();
doReturn(true).when(activity1).shouldBeVisibleUnchecked();
appWin.setHasSurface(true);
appWin1.setHasSurface(true);
@@ -934,28 +937,28 @@
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
- final Task stack = new TaskBuilder(mSupervisor)
+ final Task rootTask = new TaskBuilder(mSupervisor)
.setDisplay(dc)
.setCreateActivity(true)
.build();
- doReturn(true).when(stack).isVisible();
+ doReturn(true).when(rootTask).isVisible();
- final Task freeformStack = new TaskBuilder(mSupervisor)
+ final Task freeformRootTask = new TaskBuilder(mSupervisor)
.setDisplay(dc)
.setCreateActivity(true)
.setWindowingMode(WINDOWING_MODE_FREEFORM)
.build();
- doReturn(true).when(freeformStack).isVisible();
- freeformStack.getTopChild().setBounds(100, 100, 300, 400);
+ doReturn(true).when(freeformRootTask).isVisible();
+ freeformRootTask.getTopChild().setBounds(100, 100, 300, 400);
assertTrue(dc.getDefaultTaskDisplayArea().isRootTaskVisible(WINDOWING_MODE_FREEFORM));
- freeformStack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- stack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ freeformRootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ rootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation());
- stack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- freeformStack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ rootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ freeformRootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
}
@@ -1683,66 +1686,6 @@
}
@Test
- public void testGetOrCreateRootHomeTask_defaultDisplay() {
- TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea();
-
- // Remove the current home stack if it exists so a new one can be created below.
- Task homeTask = defaultTaskDisplayArea.getRootHomeTask();
- if (homeTask != null) {
- defaultTaskDisplayArea.removeChild(homeTask);
- }
- assertNull(defaultTaskDisplayArea.getRootHomeTask());
-
- assertNotNull(defaultTaskDisplayArea.getOrCreateRootHomeTask());
- }
-
- @Test
- public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() {
- DisplayContent display = createNewDisplay();
- doReturn(true).when(display).supportsSystemDecorations();
-
- // Remove the current home stack if it exists so a new one can be created below.
- TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
- Task homeTask = taskDisplayArea.getRootHomeTask();
- if (homeTask != null) {
- taskDisplayArea.removeChild(homeTask);
- }
- assertNull(taskDisplayArea.getRootHomeTask());
-
- assertNotNull(taskDisplayArea.getOrCreateRootHomeTask());
- }
-
- @Test
- public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() {
- DisplayContent display = createNewDisplay();
- TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
- doReturn(false).when(display).supportsSystemDecorations();
-
- assertNull(taskDisplayArea.getRootHomeTask());
- assertNull(taskDisplayArea.getOrCreateRootHomeTask());
- }
-
- @Test
- public void testGetOrCreateRootHomeTask_untrustedDisplay() {
- DisplayContent display = createNewDisplay();
- TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
- doReturn(false).when(display).isTrusted();
-
- assertNull(taskDisplayArea.getRootHomeTask());
- assertNull(taskDisplayArea.getOrCreateRootHomeTask());
- }
-
- @Test
- public void testGetOrCreateRootHomeTask_dontMoveToTop() {
- DisplayContent display = createNewDisplay();
- display.mDontMoveToTop = true;
- TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
-
- assertNull(taskDisplayArea.getRootHomeTask());
- assertNull(taskDisplayArea.getOrCreateRootHomeTask());
- }
-
- @Test
public void testValidWindowingLayer() {
final SurfaceControl windowingLayer = mDisplayContent.getWindowingLayer();
assertNotNull(windowingLayer);
@@ -1761,8 +1704,8 @@
@Test
public void testFindScrollCaptureTargetWindow_behindWindow() {
DisplayContent display = createNewDisplay();
- Task stack = createTaskStackOnDisplay(display);
- Task task = createTaskInStack(stack, 0 /* userId */);
+ Task rootTask = createTask(display);
+ Task task = createTaskInRootTask(rootTask, 0 /* userId */);
WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
@@ -1774,8 +1717,8 @@
@Test
public void testFindScrollCaptureTargetWindow_cantReceiveKeys() {
DisplayContent display = createNewDisplay();
- Task stack = createTaskStackOnDisplay(display);
- Task task = createTaskInStack(stack, 0 /* userId */);
+ Task rootTask = createTask(display);
+ Task task = createTaskInRootTask(rootTask, 0 /* userId */);
WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
WindowState invisible = createWindow(null, TYPE_APPLICATION, "invisible");
invisible.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false
@@ -1788,8 +1731,8 @@
@Test
public void testFindScrollCaptureTargetWindow_taskId() {
DisplayContent display = createNewDisplay();
- Task stack = createTaskStackOnDisplay(display);
- Task task = createTaskInStack(stack, 0 /* userId */);
+ Task rootTask = createTask(display);
+ Task task = createTaskInRootTask(rootTask, 0 /* userId */);
WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
@@ -1800,8 +1743,8 @@
@Test
public void testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys() {
DisplayContent display = createNewDisplay();
- Task stack = createTaskStackOnDisplay(display);
- Task task = createTaskInStack(stack, 0 /* userId */);
+ Task rootTask = createTask(display);
+ Task task = createTaskInRootTask(rootTask, 0 /* userId */);
WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
window.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false
WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
@@ -1828,7 +1771,7 @@
@Test
public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() {
final DisplayContent dc = createNewDisplay();
- final Task stack = new TaskBuilder(mSupervisor)
+ final Task rootTask = new TaskBuilder(mSupervisor)
.setDisplay(dc)
.build();
doAnswer(invocation -> {
@@ -1837,7 +1780,7 @@
assertEquals(config.windowConfiguration.getWindowingMode(),
config.windowConfiguration.getDisplayWindowingMode());
return null;
- }).when(stack).onConfigurationChanged(any());
+ }).when(rootTask).onConfigurationChanged(any());
dc.setWindowingMode(WINDOWING_MODE_FREEFORM);
dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
}
@@ -2072,6 +2015,130 @@
0 /* delta */);
}
+ /**
+ * Verifies {@link DisplayContent#remove} should not resume home root task on the removing
+ * display.
+ */
+ @Test
+ public void testNotResumeHomeRootTaskOnRemovingDisplay() {
+ // Create a display which supports system decoration and allows reparenting root tasks to
+ // another display when the display is removed.
+ final DisplayContent display = new TestDisplayContent.Builder(
+ mAtm, 1000, 1500).setSystemDecorations(true).build();
+ doReturn(false).when(display).shouldDestroyContentOnRemove();
+
+ // Put home root task on the display.
+ final Task homeRootTask = new TaskBuilder(mSupervisor)
+ .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build();
+
+ // Put a finishing standard activity which will be reparented.
+ final Task rootTask = createTaskWithActivity(display.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */);
+ rootTask.topRunningActivity().makeFinishingLocked();
+
+ clearInvocations(homeRootTask);
+ display.remove();
+
+ // The removed display should have no focused root task and its home root task should never
+ // resume.
+ assertNull(display.getFocusedRootTask());
+ verify(homeRootTask, never()).resumeTopActivityUncheckedLocked(any(), any());
+ }
+
+ /**
+ * Verifies the correct activity is returned when querying the top running activity.
+ */
+ @Test
+ public void testTopRunningActivity() {
+ final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+ final KeyguardController keyguard = mSupervisor.getKeyguardController();
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ final ActivityRecord activity = rootTask.getTopNonFinishingActivity();
+
+ // Create empty root task on top.
+ final Task emptyRootTask = new TaskBuilder(mSupervisor).build();
+
+ // Make sure the top running activity is not affected when keyguard is not locked.
+ assertTopRunningActivity(activity, display);
+
+ // Check to make sure activity not reported when it cannot show on lock and lock is on.
+ doReturn(true).when(keyguard).isKeyguardLocked();
+ assertEquals(activity, display.topRunningActivity());
+ assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+ // Move root task with activity to top.
+ rootTask.moveToFront("testRootTaskToFront");
+ assertEquals(rootTask, display.getFocusedRootTask());
+ assertEquals(activity, display.topRunningActivity());
+ assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+ // Add activity that should be shown on the keyguard.
+ final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mAtm)
+ .setTask(rootTask)
+ .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
+ .build();
+
+ // Ensure the show when locked activity is returned.
+ assertTopRunningActivity(showWhenLockedActivity, display);
+
+ // Move empty root task to front. The running activity in focusable root task which below
+ // the empty root task should be returned.
+ emptyRootTask.moveToFront("emptyRootTaskToFront");
+ assertEquals(rootTask, display.getFocusedRootTask());
+ assertTopRunningActivity(showWhenLockedActivity, display);
+ }
+
+ private static void assertTopRunningActivity(ActivityRecord top, DisplayContent display) {
+ assertEquals(top, display.topRunningActivity());
+ assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
+ }
+
+ @Test
+ public void testRemoveRootTaskInWindowingModes() {
+ removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes(
+ WINDOWING_MODE_FULLSCREEN));
+ }
+
+ @Test
+ public void testRemoveRootTaskWithActivityTypes() {
+ removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes(
+ ACTIVITY_TYPE_STANDARD));
+ }
+
+ private void removeRootTaskTests(Runnable runnable) {
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, ON_TOP);
+ final Task rootTask2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, ON_TOP);
+ final Task rootTask3 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, ON_TOP);
+ final Task rootTask4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, ON_TOP);
+ final Task task1 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask1).build();
+ final Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask2).build();
+ final Task task3 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask3).build();
+ final Task task4 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask4).build();
+
+ // Reordering root tasks while removing root tasks.
+ doAnswer(invocation -> {
+ taskDisplayArea.positionChildAt(POSITION_TOP, rootTask3, false /*includingParents*/);
+ return true;
+ }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
+
+ // Removing root tasks from the display while removing root tasks.
+ doAnswer(invocation -> {
+ taskDisplayArea.removeRootTask(rootTask2);
+ return true;
+ }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
+
+ runnable.run();
+ verify(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
+ verify(mSupervisor).removeTask(eq(task3), anyBoolean(), anyBoolean(), any());
+ verify(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
+ verify(mSupervisor).removeTask(eq(task1), anyBoolean(), anyBoolean(), any());
+ }
+
private boolean isOptionsPanelAtRight(int displayId) {
return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 4e2697a..1bddd7b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -17,8 +17,6 @@
package com.android.server.wm;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
@@ -124,9 +122,8 @@
*/
private WindowState createDropTargetWindow(String name, int ownerId) {
final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
- final Task stack = createTaskStackOnDisplay(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
- final Task task = createTaskInStack(stack, ownerId);
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, ownerId);
task.addChild(activity, 0);
// Use a new TestIWindow so we don't collect events for other windows
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index f97e794..7d137bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -488,9 +488,9 @@
@Test
public void testIsAnimatingByRecents() {
final ActivityRecord homeActivity = createHomeActivity();
- final Task rootTask = createTaskStackOnDisplay(mDefaultDisplay);
- final Task childTask = createTaskInStack(rootTask, 0 /* userId */);
- final Task leafTask = createTaskInStack(childTask, 0 /* userId */);
+ final Task rootTask = createTask(mDefaultDisplay);
+ final Task childTask = createTaskInRootTask(rootTask, 0 /* userId */);
+ final Task leafTask = createTaskInRootTask(childTask, 0 /* userId */);
spyOn(leafTask);
doReturn(true).when(leafTask).isVisible();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
deleted file mode 100644
index 8388f2a..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ /dev/null
@@ -1,953 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.TYPE_VIRTUAL;
-import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-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.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.contains;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.refEq;
-
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.platform.test.annotations.Presubmit;
-import android.util.MergedConfiguration;
-import android.util.Pair;
-
-import androidx.test.filters.MediumTest;
-
-import com.android.internal.app.ResolverActivity;
-import com.android.server.wm.Task.ActivityState;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Tests for the {@link RootWindowContainer} class.
- *
- * Build/Install/Run:
- * atest WmTests:RootActivityContainerTests
- */
-@MediumTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class RootActivityContainerTests extends WindowTestsBase {
-
- @Before
- public void setUp() throws Exception {
- doNothing().when(mAtm).updateSleepIfNeededLocked();
- }
-
- /**
- * This test ensures that we do not try to restore a task based off an invalid task id. We
- * should expect {@code null} to be returned in this case.
- */
- @Test
- public void testRestoringInvalidTask() {
- mRootWindowContainer.getDefaultDisplay().removeAllTasks();
- Task task = mRootWindowContainer.anyTaskForId(0 /*taskId*/,
- MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
- assertNull(task);
- }
-
- /**
- * This test ensures that an existing task in the pinned stack is moved to the fullscreen
- * activity stack when a new task is added.
- */
- @Test
- public void testReplacingTaskInPinnedStack() {
- Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
- .setTask(fullscreenTask).build();
- final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
- .setTask(fullscreenTask).build();
-
- fullscreenTask.moveToFront("testReplacingTaskInPinnedStack");
-
- // Ensure full screen stack has both tasks.
- ensureStackPlacement(fullscreenTask, firstActivity, secondActivity);
-
- // Move first activity to pinned stack.
- mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove");
-
- final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
- Task pinnedStack = taskDisplayArea.getRootPinnedTask();
- // Ensure a task has moved over.
- ensureStackPlacement(pinnedStack, firstActivity);
- ensureStackPlacement(fullscreenTask, secondActivity);
-
- // Move second activity to pinned stack.
- mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove");
-
- // Need to get stacks again as a new instance might have been created.
- pinnedStack = taskDisplayArea.getRootPinnedTask();
- fullscreenTask = taskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD);
- // Ensure stacks have swapped tasks.
- ensureStackPlacement(pinnedStack, secondActivity);
- ensureStackPlacement(fullscreenTask, firstActivity);
- }
-
- @Test
- public void testMovingBottomMostStackActivityToPinnedStack() {
- final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
- .setTask(fullscreenTask).build();
- final Task task = firstActivity.getTask();
-
- final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
- .setTask(fullscreenTask).build();
-
- fullscreenTask.moveTaskToBack(task);
-
- // Ensure full screen stack has both tasks.
- ensureStackPlacement(fullscreenTask, firstActivity, secondActivity);
- assertEquals(task.getTopMostActivity(), secondActivity);
- firstActivity.setState(STOPPED, "testMovingBottomMostStackActivityToPinnedStack");
-
-
- // Move first activity to pinned stack.
- mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove");
-
- assertTrue(firstActivity.mRequestForceTransition);
- }
-
- private static void ensureStackPlacement(Task task, ActivityRecord... activities) {
- final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();
-
- task.forAllActivities((Consumer<ActivityRecord>) taskActivities::add, false);
-
- assertEquals("Expecting " + Arrays.deepToString(activities) + " got " + taskActivities,
- taskActivities.size(), activities != null ? activities.length : 0);
-
- if (activities == null) {
- return;
- }
-
- for (ActivityRecord activity : activities) {
- assertTrue(taskActivities.contains(activity));
- }
- }
-
- @Test
- public void testApplySleepTokens() {
- final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
- final KeyguardController keyguard = mSupervisor.getKeyguardController();
- final Task stack = new TaskBuilder(mSupervisor)
- .setDisplay(display)
- .setOnTop(false)
- .build();
-
- // Make sure we wake and resume in the case the display is turning on and the keyguard is
- // not showing.
- verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
- false /* displayShouldSleep */, true /* isFocusedStack */,
- false /* keyguardShowing */, true /* expectWakeFromSleep */,
- true /* expectResumeTopActivity */);
-
- // Make sure we wake and don't resume when the display is turning on and the keyguard is
- // showing.
- verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
- false /* displayShouldSleep */, true /* isFocusedStack */,
- true /* keyguardShowing */, true /* expectWakeFromSleep */,
- false /* expectResumeTopActivity */);
-
- // Make sure we wake and don't resume when the display is turning on and the keyguard is
- // not showing as unfocused.
- verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
- false /* displayShouldSleep */, false /* isFocusedStack */,
- false /* keyguardShowing */, true /* expectWakeFromSleep */,
- false /* expectResumeTopActivity */);
-
- // Should not do anything if the display state hasn't changed.
- verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
- false /* displayShouldSleep */, true /* isFocusedStack */,
- false /* keyguardShowing */, false /* expectWakeFromSleep */,
- false /* expectResumeTopActivity */);
- }
-
- private void verifySleepTokenBehavior(DisplayContent display, KeyguardController keyguard,
- Task stack, boolean displaySleeping, boolean displayShouldSleep,
- boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
- boolean expectResumeTopActivity) {
- reset(stack);
-
- doReturn(displayShouldSleep).when(display).shouldSleep();
- doReturn(displaySleeping).when(display).isSleeping();
- doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
-
- doReturn(isFocusedStack).when(stack).isFocusedRootTaskOnDisplay();
- doReturn(isFocusedStack ? stack : null).when(display).getFocusedRootTask();
- TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea();
- doReturn(isFocusedStack ? stack : null).when(defaultTaskDisplayArea).getFocusedRootTask();
- mRootWindowContainer.applySleepTokens(true);
- verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
- verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
- null /* target */, null /* targetOptions */);
- }
-
- @Test
- public void testAwakeFromSleepingWithAppConfiguration() {
- final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
- final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
- activity.moveFocusableActivityToTop("test");
- assertTrue(activity.getRootTask().isFocusedRootTaskOnDisplay());
- ActivityRecordTests.setRotatedScreenOrientationSilently(activity);
-
- final Configuration rotatedConfig = new Configuration();
- display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation()
- .rotationForOrientation(activity.getOrientation(), display.getRotation()));
- assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation);
- // Assume the activity was shown in different orientation. For example, the top activity is
- // landscape and the portrait lockscreen is shown.
- activity.setLastReportedConfiguration(
- new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig));
- activity.setState(ActivityState.STOPPED, "sleep");
-
- display.setIsSleeping(true);
- doReturn(false).when(display).shouldSleep();
- // Allow to resume when awaking.
- setBooted(mAtm);
- mRootWindowContainer.applySleepTokens(true);
-
- // The display orientation should be changed by the activity so there is no relaunch.
- verify(activity, never()).relaunchActivityLocked(anyBoolean());
- assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation);
- }
-
- /**
- * Verifies that removal of activity with task and stack is done correctly.
- */
- @Test
- public void testRemovingStackOnAppCrash() {
- final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
- .getDefaultTaskDisplayArea();
- final int originalStackCount = defaultTaskDisplayArea.getRootTaskCount();
- final Task stack = defaultTaskDisplayArea.createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(stack).build();
-
- assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getRootTaskCount());
-
- // Let's pretend that the app has crashed.
- firstActivity.app.setThread(null);
- mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
-
- // Verify that the stack was removed.
- assertEquals(originalStackCount, defaultTaskDisplayArea.getRootTaskCount());
- }
-
- /**
- * Verifies that removal of activities with task and stack is done correctly when there are
- * several task display areas.
- */
- @Test
- public void testRemovingStackOnAppCrash_multipleDisplayAreas() {
- final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
- .getDefaultTaskDisplayArea();
- final int originalStackCount = defaultTaskDisplayArea.getRootTaskCount();
- final Task stack = defaultTaskDisplayArea.createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(stack).build();
- assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getRootTaskCount());
-
- final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent();
- final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
- dc, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST);
- final Task secondStack = secondTaskDisplayArea.createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- new ActivityBuilder(mAtm).setTask(secondStack).setUseProcess(firstActivity.app).build();
- assertEquals(1, secondTaskDisplayArea.getRootTaskCount());
-
- // Let's pretend that the app has crashed.
- firstActivity.app.setThread(null);
- mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
-
- // Verify that the stacks were removed.
- assertEquals(originalStackCount, defaultTaskDisplayArea.getRootTaskCount());
- assertEquals(0, secondTaskDisplayArea.getRootTaskCount());
- }
-
- @Test
- public void testFocusability() {
- final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
- .getDefaultTaskDisplayArea();
- final Task stack = defaultTaskDisplayArea.createRootTask(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
-
- // Created stacks are focusable by default.
- assertTrue(stack.isTopActivityFocusable());
- assertTrue(activity.isFocusable());
-
- // If the stack is made unfocusable, its activities should inherit that.
- stack.setFocusable(false);
- assertFalse(stack.isTopActivityFocusable());
- assertFalse(activity.isFocusable());
-
- final Task pinnedStack = defaultTaskDisplayArea.createRootTask(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
- .setTask(pinnedStack).build();
-
- // We should not be focusable when in pinned mode
- assertFalse(pinnedStack.isTopActivityFocusable());
- assertFalse(pinnedActivity.isFocusable());
-
- // Add flag forcing focusability.
- pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
-
- // Task with FLAG_ALWAYS_FOCUSABLE should be focusable.
- assertTrue(pinnedStack.isTopActivityFocusable());
- assertTrue(pinnedActivity.isFocusable());
- }
-
- /**
- * Verify that split-screen primary stack will be chosen if activity is launched that targets
- * split-screen secondary, but a matching existing instance is found on top of split-screen
- * primary stack.
- */
- @Test
- public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
- // Create primary split-screen stack with a task and an activity.
- final Task primaryStack = mRootWindowContainer.getDefaultTaskDisplayArea()
- .createRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task task = new TaskBuilder(mSupervisor).setParentTask(primaryStack).build();
- final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build();
-
- // Find a launch stack for the top activity in split-screen primary, while requesting
- // split-screen secondary.
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
- final Task result =
- mRootWindowContainer.getLaunchRootTask(r, options, task, true /* onTop */);
-
- // Assert that the primary stack is returned.
- assertEquals(primaryStack, result);
- }
-
- /**
- * Verify that home stack would be moved to front when the top activity is Recents.
- */
- @Test
- public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
- // Create stack/task on default display.
- final Task targetStack = new TaskBuilder(mSupervisor)
- .setCreateActivity(true)
- .setOnTop(false)
- .build();
- final Task targetTask = targetStack.getBottomMostTask();
-
- // Create Recents on top of the display.
- final Task stack = new TaskBuilder(mSupervisor)
- .setCreateActivity(true)
- .setActivityType(ACTIVITY_TYPE_RECENTS)
- .build();
-
- final String reason = "findTaskToMoveToFront";
- mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
- false);
-
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- verify(taskDisplayArea).moveHomeRootTaskToFront(contains(reason));
- }
-
- /**
- * Verify that home stack won't be moved to front if the top activity on other display is
- * Recents.
- */
- @Test
- public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
- // Create tasks on default display.
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final Task targetRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetRootTask).build();
-
- // Create Recents on secondary display.
- final TestDisplayContent secondDisplay = addNewDisplayContentAt(
- DisplayContent.POSITION_TOP);
- final Task rootTask = secondDisplay.getDefaultTaskDisplayArea()
- .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
- new ActivityBuilder(mAtm).setTask(rootTask).build();
-
- final String reason = "findTaskToMoveToFront";
- mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
- false);
-
- verify(taskDisplayArea, never()).moveHomeRootTaskToFront(contains(reason));
- }
-
- /**
- * Verify if a stack is not at the topmost position, it should be able to resume its activity if
- * the stack is the top focused.
- */
- @Test
- public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
- // Create a root task at bottom.
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, false /* onTop */));
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
- taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
-
- // Assume the task is not at the topmost position (e.g. behind always-on-top stacks) but it
- // is the current top focused task.
- assertFalse(rootTask.isTopRootTaskInDisplayArea());
- doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
-
- // Use the task as target to resume.
- mRootWindowContainer.resumeFocusedTasksTopActivities(
- rootTask, activity, null /* targetOptions */);
-
- // Verify the target task should resume its activity.
- verify(rootTask, times(1)).resumeTopActivityUncheckedLocked(
- eq(activity), eq(null /* targetOptions */));
- }
-
- /**
- * Verify that home activity will be started on a display even if another display has a
- * focusable activity.
- */
- @Test
- public void testResumeFocusedStacksStartsHomeActivity_NoActivities() {
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- taskDisplayArea.getRootHomeTask().removeIfPossible();
- taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-
- doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
-
- mAtm.setBooted(true);
-
- // Trigger resume on all displays
- mRootWindowContainer.resumeFocusedTasksTopActivities();
-
- // Verify that home activity was started on the default display
- verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea));
- }
-
- /**
- * Verify that home activity will be started on a display even if another display has a
- * focusable activity.
- */
- @Test
- public void testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen() {
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- taskDisplayArea.getRootHomeTask().removeIfPossible();
- taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-
- // Create an activity on secondary display.
- final TestDisplayContent secondDisplay = addNewDisplayContentAt(
- DisplayContent.POSITION_TOP);
- final Task rootTask = secondDisplay.getDefaultTaskDisplayArea().createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- new ActivityBuilder(mAtm).setTask(rootTask).build();
-
- doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
-
- mAtm.setBooted(true);
-
- // Trigger resume on all displays
- mRootWindowContainer.resumeFocusedTasksTopActivities();
-
- // Verify that home activity was started on the default display
- verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea));
- }
-
- /**
- * Verify that a lingering transition is being executed in case the activity to be resumed is
- * already resumed
- */
- @Test
- public void testResumeActivityLingeringTransition() {
- // Create a root task at top.
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, false /* onTop */));
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(rootTask).setOnTop(true).build();
- activity.setState(ActivityState.RESUMED, "test");
-
- // Assume the task is at the topmost position
- assertTrue(rootTask.isTopRootTaskInDisplayArea());
-
- // Use the task as target to resume.
- mRootWindowContainer.resumeFocusedTasksTopActivities();
-
- // Verify the lingering app transition is being executed because it's already resumed
- verify(rootTask, times(1)).executeAppTransition(any());
- }
-
- @Test
- public void testResumeActivityLingeringTransition_notExecuted() {
- // Create a root task at bottom.
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, false /* onTop */));
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(rootTask).setOnTop(true).build();
- activity.setState(ActivityState.RESUMED, "test");
- taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
-
- // Assume the task is at the topmost position
- assertFalse(rootTask.isTopRootTaskInDisplayArea());
- doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
-
- // Use the task as target to resume.
- mRootWindowContainer.resumeFocusedTasksTopActivities();
-
- // Verify the lingering app transition is being executed because it's already resumed
- verify(rootTask, never()).executeAppTransition(any());
- }
-
- /**
- * Tests that home activities can be started on the displays that supports system decorations.
- */
- @Test
- public void testStartHomeOnAllDisplays() {
- mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
- mockResolveSecondaryHomeActivity();
-
- // Create secondary displays.
- final TestDisplayContent secondDisplay =
- new TestDisplayContent.Builder(mAtm, 1000, 1500)
- .setSystemDecorations(true).build();
-
- doReturn(true).when(mRootWindowContainer)
- .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
- anyBoolean());
-
- mRootWindowContainer.startHomeOnAllDisplays(0, "testStartHome");
-
- assertTrue(mRootWindowContainer.getDefaultDisplay().getTopRootTask().isActivityTypeHome());
- assertNotNull(secondDisplay.getTopRootTask());
- assertTrue(secondDisplay.getTopRootTask().isActivityTypeHome());
- }
-
- /**
- * Tests that home activities won't be started before booting when display added.
- */
- @Test
- public void testNotStartHomeBeforeBoot() {
- final int displayId = 1;
- final boolean isBooting = mAtm.mAmInternal.isBooting();
- final boolean isBooted = mAtm.mAmInternal.isBooted();
- try {
- mAtm.mAmInternal.setBooting(false);
- mAtm.mAmInternal.setBooted(false);
- mRootWindowContainer.onDisplayAdded(displayId);
- verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
- } finally {
- mAtm.mAmInternal.setBooting(isBooting);
- mAtm.mAmInternal.setBooted(isBooted);
- }
- }
-
- /**
- * Tests whether home can be started if being instrumented.
- */
- @Test
- public void testCanStartHomeWhenInstrumented() {
- final ActivityInfo info = new ActivityInfo();
- info.applicationInfo = new ApplicationInfo();
- final WindowProcessController app = mock(WindowProcessController.class);
- doReturn(app).when(mAtm).getProcessController(any(), anyInt());
-
- // Can not start home if we don't want to start home while home is being instrumented.
- doReturn(true).when(app).isInstrumenting();
- final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
- .getDefaultTaskDisplayArea();
- assertFalse(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
- false /* allowInstrumenting*/));
-
- // Can start home for other cases.
- assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
- true /* allowInstrumenting*/));
-
- doReturn(false).when(app).isInstrumenting();
- assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
- false /* allowInstrumenting*/));
- assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
- true /* allowInstrumenting*/));
- }
-
- /**
- * Tests that secondary home activity should not be resolved if device is still locked.
- */
- @Test
- public void testStartSecondaryHomeOnDisplayWithUserKeyLocked() {
- // Create secondary displays.
- final TestDisplayContent secondDisplay =
- new TestDisplayContent.Builder(mAtm, 1000, 1500)
- .setSystemDecorations(true).build();
-
- // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false.
- final int currentUser = mRootWindowContainer.mCurrentUser;
- mRootWindowContainer.mCurrentUser = -1;
-
- mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome",
- secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */);
-
- try {
- verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any());
- } finally {
- mRootWindowContainer.mCurrentUser = currentUser;
- }
- }
-
- /**
- * Tests that secondary home activity should not be resolved if display does not support system
- * decorations.
- */
- @Test
- public void testStartSecondaryHomeOnDisplayWithoutSysDecorations() {
- // Create secondary displays.
- final TestDisplayContent secondDisplay =
- new TestDisplayContent.Builder(mAtm, 1000, 1500)
- .setSystemDecorations(false).build();
-
- mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome",
- secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */);
-
- verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any());
- }
-
- /**
- * Tests that when starting {@link #ResolverActivity} for home, it should use the standard
- * activity type (in a new stack) so the order of back stack won't be broken.
- */
- @Test
- public void testStartResolverActivityForHome() {
- final ActivityInfo info = new ActivityInfo();
- info.applicationInfo = new ApplicationInfo();
- info.applicationInfo.packageName = "android";
- info.name = ResolverActivity.class.getName();
- doReturn(info).when(mRootWindowContainer).resolveHomeActivity(anyInt(), any());
-
- mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
- final ActivityRecord resolverActivity = mRootWindowContainer.topRunningActivity();
-
- assertEquals(info, resolverActivity.info);
- assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getRootTask().getActivityType());
- }
-
- /**
- * Tests that secondary home should be selected if primary home not set.
- */
- @Test
- public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() {
- // Setup: primary home not set.
- final Intent primaryHomeIntent = mAtm.getHomeIntent();
- final ActivityInfo aInfoPrimary = new ActivityInfo();
- aInfoPrimary.name = ResolverActivity.class.getName();
- doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(primaryHomeIntent));
- // Setup: set secondary home.
- mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
-
- // Run the test.
- final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
- .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
- final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
- assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
- assertEquals(aInfoSecondary.applicationInfo.packageName,
- resolvedInfo.first.applicationInfo.packageName);
- }
-
- /**
- * Tests that the default secondary home activity is always picked when it is in forced by
- * config_useSystemProvidedLauncherForSecondary.
- */
- @Test
- public void testResolveSecondaryHomeActivityForced() {
- // SetUp: set primary home.
- mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
- // SetUp: set secondary home and force it.
- mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */);
- final Intent secondaryHomeIntent =
- mAtm.getSecondaryHomeIntent(null /* preferredPackage */);
- final List<ResolveInfo> resolutions = new ArrayList<>();
- final ResolveInfo resolveInfo = new ResolveInfo();
- final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
- resolveInfo.activityInfo = aInfoSecondary;
- resolutions.add(resolveInfo);
- doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(),
- refEq(secondaryHomeIntent));
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
- anyBoolean());
-
- // Run the test.
- final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
- .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
- assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
- assertEquals(aInfoSecondary.applicationInfo.packageName,
- resolvedInfo.first.applicationInfo.packageName);
- }
-
- /**
- * Tests that secondary home should be selected if primary home not support secondary displays
- * or there is no matched activity in the same package as selected primary home.
- */
- @Test
- public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() {
- // Setup: there is no matched activity in the same package as selected primary home.
- mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
- final List<ResolveInfo> resolutions = new ArrayList<>();
- doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
- // Setup: set secondary home.
- mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
-
- // Run the test.
- final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
- .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
- final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
- assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
- assertEquals(aInfoSecondary.applicationInfo.packageName,
- resolvedInfo.first.applicationInfo.packageName);
- }
- /**
- * Tests that primary home activity should be selected if it already support secondary displays.
- */
- @Test
- public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() {
- // SetUp: set primary home.
- mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
- // SetUp: put primary home info on 2nd item
- final List<ResolveInfo> resolutions = new ArrayList<>();
- final ResolveInfo infoFake1 = new ResolveInfo();
- infoFake1.activityInfo = new ActivityInfo();
- infoFake1.activityInfo.name = "fakeActivity1";
- infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
- infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
- final ResolveInfo infoFake2 = new ResolveInfo();
- final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */);
- infoFake2.activityInfo = aInfoPrimary;
- resolutions.add(infoFake1);
- resolutions.add(infoFake2);
- doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
-
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
- anyBoolean());
-
- // Run the test.
- final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
- .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
- assertEquals(aInfoPrimary.name, resolvedInfo.first.name);
- assertEquals(aInfoPrimary.applicationInfo.packageName,
- resolvedInfo.first.applicationInfo.packageName);
- }
-
- /**
- * Tests that the first one that matches should be selected if there are multiple activities.
- */
- @Test
- public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
- // SetUp: set primary home.
- mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
- // Setup: prepare two eligible activity info.
- final List<ResolveInfo> resolutions = new ArrayList<>();
- final ResolveInfo infoFake1 = new ResolveInfo();
- infoFake1.activityInfo = new ActivityInfo();
- infoFake1.activityInfo.name = "fakeActivity1";
- infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
- infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
- final ResolveInfo infoFake2 = new ResolveInfo();
- infoFake2.activityInfo = new ActivityInfo();
- infoFake2.activityInfo.name = "fakeActivity2";
- infoFake2.activityInfo.applicationInfo = new ApplicationInfo();
- infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2";
- resolutions.add(infoFake1);
- resolutions.add(infoFake2);
- doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
-
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
- anyBoolean());
-
- // Use the first one of matched activities in the same package as selected primary home.
- final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
- .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
-
- assertEquals(infoFake1.activityInfo.applicationInfo.packageName,
- resolvedInfo.first.applicationInfo.packageName);
- assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
- }
-
- /**
- * Test that {@link RootWindowContainer#getLaunchRootTask} with the real caller id will get the
- * expected stack when requesting the activity launch on the secondary display.
- */
- @Test
- public void testGetLaunchStackWithRealCallerId() {
- // Create a non-system owned virtual display.
- final TestDisplayContent secondaryDisplay =
- new TestDisplayContent.Builder(mAtm, 1000, 1500)
- .setType(TYPE_VIRTUAL).setOwnerUid(100).build();
-
- // Create an activity with specify the original launch pid / uid.
- final ActivityRecord r = new ActivityBuilder(mAtm).setLaunchedFromPid(200)
- .setLaunchedFromUid(200).build();
-
- // Simulate ActivityStarter to find a launch stack for requesting the activity to launch
- // on the secondary display with realCallerId.
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchDisplayId(secondaryDisplay.mDisplayId);
- options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
- doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId,
- 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info);
- final Task result = mRootWindowContainer.getLaunchRootTask(r, options,
- null /* task */, true /* onTop */, null, 300 /* test realCallerPid */,
- 300 /* test realCallerUid */);
-
- // Assert that the stack is returned as expected.
- assertNotNull(result);
- assertEquals("The display ID of the stack should same as secondary display ",
- secondaryDisplay.mDisplayId, result.getDisplayId());
- }
-
- @Test
- public void testGetValidLaunchStackOnDisplayWithCandidateRootTask() {
- // Create a root task with an activity on secondary display.
- final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300,
- 600).build();
- final Task task = new TaskBuilder(mSupervisor)
- .setDisplay(secondaryDisplay).build();
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
-
- // Make sure the root task is valid and can be reused on default display.
- final Task stack = mRootWindowContainer.getValidLaunchRootTaskInTaskDisplayArea(
- mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task,
- null /* options */, null /* launchParams */);
- assertEquals(task, stack);
- }
-
- @Test
- public void testSwitchUser_missingHomeRootTask() {
- final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- doReturn(fullscreenTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
-
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- Task homeStack = taskDisplayArea.getRootHomeTask();
- if (homeStack != null) {
- homeStack.removeImmediately();
- }
- assertNull(taskDisplayArea.getRootHomeTask());
-
- int currentUser = mRootWindowContainer.mCurrentUser;
- int otherUser = currentUser + 1;
-
- mRootWindowContainer.switchUser(otherUser, null);
-
- assertNotNull(taskDisplayArea.getRootHomeTask());
- assertEquals(taskDisplayArea.getTopRootTask(), taskDisplayArea.getRootHomeTask());
- }
-
- /**
- * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
- * info for test cases.
- *
- * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use
- * secondary home intent.
- * @param forceSystemProvided Indicate to force using system provided home activity.
- */
- private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) {
- ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome);
- Intent targetIntent;
- if (primaryHome) {
- targetIntent = mAtm.getHomeIntent();
- } else {
- Resources resources = mContext.getResources();
- spyOn(resources);
- doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString(
- com.android.internal.R.string.config_secondaryHomePackage);
- doReturn(forceSystemProvided).when(resources).getBoolean(
- com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
- targetIntent = mAtm.getSecondaryHomeIntent(null /* preferredPackage */);
- }
- doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(targetIntent));
- }
-
- /**
- * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent
- * activity info for test cases.
- */
- private void mockResolveSecondaryHomeActivity() {
- final Intent secondaryHomeIntent = mAtm
- .getSecondaryHomeIntent(null /* preferredPackage */);
- final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false);
- doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer)
- .resolveSecondaryHomeActivity(anyInt(), any());
- }
-
- private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) {
- final ActivityInfo aInfo = new ActivityInfo();
- aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity";
- aInfo.applicationInfo = new ApplicationInfo();
- aInfo.applicationInfo.packageName =
- primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage";
- return aInfo;
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
similarity index 60%
rename from services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
rename to services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 3a7954b..748622b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.server.wm;
@@ -27,12 +27,14 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
@@ -49,6 +51,8 @@
import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -56,26 +60,30 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
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.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
import android.app.ActivityManager;
import android.app.IApplicationThread;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
-import com.android.server.wm.TaskDisplayArea.OnRootTaskOrderChangedListener;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -84,15 +92,16 @@
import java.util.function.Consumer;
/**
- * Tests for the {@link ActivityStack} class.
+ * Tests for the root {@link Task} behavior.
*
* Build/Install/Run:
- * atest WmTests:ActivityStackTests
+ * atest WmTests:RootTaskTests
*/
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
-public class ActivityStackTests extends WindowTestsBase {
+public class RootTaskTests extends WindowTestsBase {
+
private TaskDisplayArea mDefaultTaskDisplayArea;
@Before
@@ -101,6 +110,172 @@
}
@Test
+ public void testRootTaskPositionChildAt() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task1 = createTaskInRootTask(rootTask, 0 /* userId */);
+ final Task task2 = createTaskInRootTask(rootTask, 1 /* userId */);
+
+ // Current user root task should be moved to top.
+ rootTask.positionChildAt(WindowContainer.POSITION_TOP, task1, false /* includingParents */);
+ assertEquals(rootTask.mChildren.get(0), task2);
+ assertEquals(rootTask.mChildren.get(1), task1);
+
+ // Non-current user won't be moved to top.
+ rootTask.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
+ assertEquals(rootTask.mChildren.get(0), task2);
+ assertEquals(rootTask.mChildren.get(1), task1);
+
+ // Non-leaf task should be moved to top regardless of the user id.
+ createTaskInRootTask(task2, 0 /* userId */);
+ createTaskInRootTask(task2, 1 /* userId */);
+ rootTask.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
+ assertEquals(rootTask.mChildren.get(0), task1);
+ assertEquals(rootTask.mChildren.get(1), task2);
+ }
+
+ @Test
+ public void testClosingAppDifferentTaskOrientation() {
+ final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
+ activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
+ activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+ final WindowContainer parent = activity1.getTask().getParent();
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation());
+ mDisplayContent.mClosingApps.add(activity2);
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parent.getOrientation());
+ }
+
+ @Test
+ public void testMoveTaskToBackDifferentTaskOrientation() {
+ final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
+ activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
+ activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+ final WindowContainer parent = activity1.getTask().getParent();
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation());
+ }
+
+ @Test
+ public void testRootTaskRemoveImmediately() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ assertEquals(rootTask, task.getRootTask());
+
+ // Remove root task and check if its child is also removed.
+ rootTask.removeImmediately();
+ assertNull(rootTask.getDisplayContent());
+ assertNull(task.getParent());
+ }
+
+ @Test
+ public void testRemoveContainer() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+
+ assertNotNull(rootTask);
+ assertNotNull(task);
+ rootTask.removeIfPossible();
+ // Assert that the container was removed.
+ assertNull(rootTask.getParent());
+ assertEquals(0, rootTask.getChildCount());
+ assertNull(rootTask.getDisplayContent());
+ assertNull(task.getDisplayContent());
+ assertNull(task.getParent());
+ }
+
+ @Test
+ public void testRemoveContainer_deferRemoval() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+
+ // Root task removal is deferred if one of its child is animating.
+ doReturn(true).when(rootTask).hasWindowsAlive();
+ doReturn(rootTask).when(task).getAnimatingContainer(
+ eq(TRANSITION | CHILDREN), anyInt());
+
+ rootTask.removeIfPossible();
+ // For the case of deferred removal the task controller will still be connected to its
+ // container until the root task window container is removed.
+ assertNotNull(rootTask.getParent());
+ assertNotEquals(0, rootTask.getChildCount());
+ assertNotNull(task);
+
+ rootTask.removeImmediately();
+ // After removing, the task will be isolated.
+ assertNull(task.getParent());
+ assertEquals(0, task.getChildCount());
+ }
+
+ @Test
+ public void testReparent() {
+ // Create first root task on primary display.
+ final Task rootTask1 = createTask(mDisplayContent);
+ final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */);
+
+ // Create second display and put second root task on it.
+ final DisplayContent dc = createNewDisplay();
+ final Task rootTask2 = createTask(dc);
+
+ // Reparent
+ clearInvocations(task1); // reset the number of onDisplayChanged for task.
+ rootTask1.reparent(dc.getDefaultTaskDisplayArea(), true /* onTop */);
+ assertEquals(dc, rootTask1.getDisplayContent());
+ final int stack1PositionInParent = rootTask1.getParent().mChildren.indexOf(rootTask1);
+ final int stack2PositionInParent = rootTask1.getParent().mChildren.indexOf(rootTask2);
+ assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
+ verify(task1, times(1)).onDisplayChanged(any());
+ }
+
+ @Test
+ public void testTaskOutset() {
+ final Task task = createTask(mDisplayContent);
+ final int taskOutset = 10;
+ spyOn(task);
+ doReturn(taskOutset).when(task).getTaskOutset();
+ doReturn(true).when(task).inMultiWindowMode();
+
+ // Mock the resolved override windowing mode to non-fullscreen
+ final WindowConfiguration windowConfiguration =
+ task.getResolvedOverrideConfiguration().windowConfiguration;
+ spyOn(windowConfiguration);
+ doReturn(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
+ .when(windowConfiguration).getWindowingMode();
+
+ // Prevent adjust task dimensions
+ doNothing().when(task).adjustForMinimalTaskDimensions(any(), any(), any());
+
+ final Rect taskBounds = new Rect(200, 200, 800, 1000);
+ // Update surface position and size by the given bounds.
+ task.setBounds(taskBounds);
+
+ assertEquals(taskBounds.width() + 2 * taskOutset, task.getLastSurfaceSize().x);
+ assertEquals(taskBounds.height() + 2 * taskOutset, task.getLastSurfaceSize().y);
+ assertEquals(taskBounds.left - taskOutset, task.getLastSurfacePosition().x);
+ assertEquals(taskBounds.top - taskOutset, task.getLastSurfacePosition().y);
+ }
+
+ @Test
+ public void testActivityAndTaskGetsProperType() {
+ final Task task1 = new TaskBuilder(mSupervisor).build();
+ ActivityRecord activity1 = createNonAttachedActivityRecord(mDisplayContent);
+
+ // First activity should become standard
+ task1.addChild(activity1, 0);
+ assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity1.getActivityType());
+ assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType());
+
+ // Second activity should also become standard
+ ActivityRecord activity2 = createNonAttachedActivityRecord(mDisplayContent);
+ task1.addChild(activity2, WindowContainer.POSITION_TOP);
+ assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity2.getActivityType());
+ assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType());
+ }
+
+ @Test
public void testResumedActivity() {
final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
final Task task = r.getTask();
@@ -113,40 +288,40 @@
@Test
public void testResumedActivityFromTaskReparenting() {
- final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+ final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
final ActivityRecord r = new ActivityBuilder(mAtm)
- .setCreateTask(true).setParentTask(parentTask).build();
+ .setCreateTask(true).setParentTask(rootTask).build();
final Task task = r.getTask();
- // Ensure moving task between two stacks updates resumed activity
+ // Ensure moving task between two root tasks updates resumed activity
r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
- assertEquals(r, parentTask.getResumedActivity());
+ assertEquals(r, rootTask.getResumedActivity());
- final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build();
- task.reparent(destStack, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
+ final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+ task.reparent(destRootTask, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
false /* animate */, true /* deferResume*/,
"testResumedActivityFromTaskReparenting");
- assertNull(parentTask.getResumedActivity());
- assertEquals(r, destStack.getResumedActivity());
+ assertNull(rootTask.getResumedActivity());
+ assertEquals(r, destRootTask.getResumedActivity());
}
@Test
public void testResumedActivityFromActivityReparenting() {
- final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+ final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
final ActivityRecord r = new ActivityBuilder(mAtm)
- .setCreateTask(true).setParentTask(parentTask).build();
+ .setCreateTask(true).setParentTask(rootTask).build();
final Task task = r.getTask();
- // Ensure moving task between two stacks updates resumed activity
+ // Ensure moving task between two root tasks updates resumed activity
r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
- assertEquals(r, parentTask.getResumedActivity());
+ assertEquals(r, rootTask.getResumedActivity());
- final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build();
- task.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
+ final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+ task.reparent(destRootTask, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
false /* animate */, false /* deferResume*/,
"testResumedActivityFromActivityReparenting");
- assertNull(parentTask.getResumedActivity());
- assertEquals(r, destStack.getResumedActivity());
+ assertNull(rootTask.getResumedActivity());
+ assertEquals(r, destRootTask.getResumedActivity());
}
@Test
@@ -155,7 +330,7 @@
// We're testing an edge case here where we have primary + fullscreen rather than secondary.
organizer.setMoveToSecondaryOnEnter(false);
- // Create primary splitscreen stack.
+ // Create primary splitscreen root task.
final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor)
.setParentTask(organizer.mPrimary)
.setOnTop(true)
@@ -168,7 +343,7 @@
primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
null /* task */);
- // Assert that stack is at the bottom.
+ // Assert that root task is at the bottom.
assertEquals(0, getTaskIndexOf(mDefaultTaskDisplayArea, primarySplitScreen));
// Ensure no longer in splitscreen.
@@ -182,7 +357,7 @@
@Test
public void testMoveToPrimarySplitScreenThenMoveToBack() {
TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
- // This time, start with a fullscreen activitystack
+ // This time, start with a fullscreen activity root task.
final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -196,7 +371,7 @@
primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack",
null /* task */);
- // Assert that stack is at the bottom.
+ // Assert that root task is at the bottom.
assertEquals(primarySplitScreen, organizer.mSecondary.getChildAt(0));
// Ensure that the override mode is restored to what it was (fullscreen)
@@ -208,7 +383,7 @@
public void testSplitScreenMoveToBack() {
TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
// Explicitly reparent task to primary split root to enter split mode, in which implies
- // primary on top and secondary containing the home task below another stack.
+ // primary on top and secondary containing the home task below another root task.
final Task primaryTask = mDefaultTaskDisplayArea.createRootTask(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask(
@@ -244,24 +419,24 @@
}
@Test
- public void testRemoveOrganizedTask_UpdateStackReference() {
+ public void testRemoveOrganizedTask_UpdateRootTaskReference() {
final Task rootHomeTask = mDefaultTaskDisplayArea.getRootHomeTask();
final ActivityRecord homeActivity = new ActivityBuilder(mAtm)
.setTask(rootHomeTask)
.build();
- final Task secondaryStack = mAtm.mTaskOrganizerController.createRootTask(
+ final Task secondaryRootTask = mAtm.mTaskOrganizerController.createRootTask(
rootHomeTask.getDisplayContent(), WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
- rootHomeTask.reparent(secondaryStack, POSITION_TOP);
- assertEquals(secondaryStack, rootHomeTask.getParent());
+ rootHomeTask.reparent(secondaryRootTask, POSITION_TOP);
+ assertEquals(secondaryRootTask, rootHomeTask.getParent());
- // This should call to {@link TaskDisplayArea#removeStackReferenceIfNeeded}.
+ // This should call to {@link TaskDisplayArea#removeRootTaskReferenceIfNeeded}.
homeActivity.removeImmediately();
assertNull(mDefaultTaskDisplayArea.getRootHomeTask());
}
@Test
- public void testStackInheritsDisplayWindowingMode() {
+ public void testRootTaskInheritsDisplayWindowingMode() {
final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -276,7 +451,7 @@
}
@Test
- public void testStackOverridesDisplayWindowingMode() {
+ public void testRootTaskOverridesDisplayWindowingMode() {
final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -358,95 +533,92 @@
}
@Test
- public void testMoveStackToBackIncludingParent() {
+ public void testMoveRootTaskToBackIncludingParent() {
final TaskDisplayArea taskDisplayArea = addNewDisplayContentAt(DisplayContent.POSITION_TOP)
.getDefaultTaskDisplayArea();
- final Task stack1 = createStackForShouldBeVisibleTest(taskDisplayArea,
+ final Task rootTask1 = createTaskForShouldBeVisibleTest(taskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */,
true /* twoLevelTask */);
- final Task stack2 = createStackForShouldBeVisibleTest(taskDisplayArea,
+ final Task rootTask2 = createTaskForShouldBeVisibleTest(taskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */,
true /* twoLevelTask */);
- // Do not move display to back because there is still another stack.
- stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask());
- verify(stack2).positionChildAtBottom(any(), eq(false) /* includingParents */);
+ // Do not move display to back because there is still another root task.
+ rootTask2.moveToBack("testMoveRootTaskToBackIncludingParent", rootTask2.getTopMostTask());
+ verify(rootTask2).positionChildAtBottom(any(), eq(false) /* includingParents */);
- // Also move display to back because there is only one stack left.
- taskDisplayArea.removeRootTask(stack1);
- stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask());
- verify(stack2).positionChildAtBottom(any(), eq(true) /* includingParents */);
+ // Also move display to back because there is only one root task left.
+ taskDisplayArea.removeRootTask(rootTask1);
+ rootTask2.moveToBack("testMoveRootTaskToBackIncludingParent", rootTask2.getTopMostTask());
+ verify(rootTask2).positionChildAtBottom(any(), eq(true) /* includingParents */);
}
@Test
public void testShouldBeVisible_Fullscreen() {
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+ // Add an activity to the pinned root task so it isn't considered empty for visibility
+ // check.
final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
- .setTask(pinnedStack)
+ .setTask(pinnedRootTask)
.build();
- assertTrue(homeStack.shouldBeVisible(null /* starting */));
- assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+ assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
+ assertTrue(pinnedRootTask.shouldBeVisible(null /* starting */));
- final Task fullscreenStack = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
- // should be visible since it is always on-top.
- doReturn(false).when(fullscreenStack).isTranslucent(any());
- assertFalse(homeStack.shouldBeVisible(null /* starting */));
- assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
- assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
+ final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ // Home root task shouldn't be visible behind an opaque fullscreen root task, but pinned
+ // root task should be visible since it is always on-top.
+ doReturn(false).when(fullscreenRootTask).isTranslucent(any());
+ assertFalse(homeRootTask.shouldBeVisible(null /* starting */));
+ assertTrue(pinnedRootTask.shouldBeVisible(null /* starting */));
+ assertTrue(fullscreenRootTask.shouldBeVisible(null /* starting */));
- // Home stack should be visible behind a translucent fullscreen stack.
- doReturn(true).when(fullscreenStack).isTranslucent(any());
- assertTrue(homeStack.shouldBeVisible(null /* starting */));
- assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
+ // Home root task should be visible behind a translucent fullscreen root task.
+ doReturn(true).when(fullscreenRootTask).isTranslucent(any());
+ assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
+ assertTrue(pinnedRootTask.shouldBeVisible(null /* starting */));
}
@Test
public void testShouldBeVisible_SplitScreen() {
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- // Home stack should always be fullscreen for this test.
- doReturn(false).when(homeStack).supportsSplitScreenWindowingMode();
- final Task splitScreenPrimary =
- createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ // Home root task should always be fullscreen for this test.
+ doReturn(false).when(homeRootTask).supportsSplitScreenWindowingMode();
+ final Task splitScreenPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final Task splitScreenSecondary =
- createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task splitScreenSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- // Home stack shouldn't be visible if both halves of split-screen are opaque.
+ // Home root task shouldn't be visible if both halves of split-screen are opaque.
doReturn(false).when(splitScreenPrimary).isTranslucent(any());
doReturn(false).when(splitScreenSecondary).isTranslucent(any());
- assertFalse(homeStack.shouldBeVisible(null /* starting */));
+ assertFalse(homeRootTask.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- // Home stack should be visible if one of the halves of split-screen is translucent.
+ // Home root task should be visible if one of the halves of split-screen is translucent.
doReturn(true).when(splitScreenPrimary).isTranslucent(any());
- assertTrue(homeStack.shouldBeVisible(null /* starting */));
+ assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- homeStack.getVisibility(null /* starting */));
+ homeRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- final Task splitScreenSecondary2 =
- createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task splitScreenSecondary2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// First split-screen secondary shouldn't be visible behind another opaque split-split
// secondary.
@@ -468,18 +640,17 @@
assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
- final Task assistantStack = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT,
- true /* onTop */);
+ final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
- // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
- doReturn(false).when(assistantStack).isTranslucent(any());
- assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+ // Split-screen root tasks shouldn't be visible behind an opaque fullscreen root task.
+ doReturn(false).when(assistantRootTask).isTranslucent(any());
+ assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
- assistantStack.getVisibility(null /* starting */));
+ assistantRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_INVISIBLE,
@@ -487,14 +658,14 @@
assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
- // Split-screen stacks should be visible behind a translucent fullscreen stack.
- doReturn(true).when(assistantStack).isTranslucent(any());
- assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+ // Split-screen root tasks should be visible behind a translucent fullscreen root task.
+ doReturn(true).when(assistantRootTask).isTranslucent(any());
+ assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
- assistantStack.getVisibility(null /* starting */));
+ assistantRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenPrimary.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -502,20 +673,20 @@
assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary2.getVisibility(null /* starting */));
- // Assistant stack shouldn't be visible behind translucent split-screen stack,
+ // Assistant root task shouldn't be visible behind translucent split-screen root task,
// unless it is configured to show on top of everything.
- doReturn(false).when(assistantStack).isTranslucent(any());
+ doReturn(false).when(assistantRootTask).isTranslucent(any());
doReturn(true).when(splitScreenPrimary).isTranslucent(any());
doReturn(true).when(splitScreenSecondary2).isTranslucent(any());
splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen");
splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen");
if (isAssistantOnTop()) {
- assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+ assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
- assistantStack.getVisibility(null /* starting */));
+ assistantRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_INVISIBLE,
@@ -523,11 +694,11 @@
assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
} else {
- assertFalse(assistantStack.shouldBeVisible(null /* starting */));
+ assertFalse(assistantRootTask.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
assertEquals(TASK_VISIBILITY_INVISIBLE,
- assistantStack.getVisibility(null /* starting */));
+ assistantRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_INVISIBLE,
@@ -539,35 +710,33 @@
@Test
public void testGetVisibility_MultiLevel() {
- final Task homeStack = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME,
- true /* onTop */);
- final Task splitPrimary = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
- ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
- final Task splitSecondary = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
- ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, true /* onTop */);
+ final Task splitPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+ final Task splitSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
- doReturn(false).when(homeStack).isTranslucent(any());
+ doReturn(false).when(homeRootTask).isTranslucent(any());
doReturn(false).when(splitPrimary).isTranslucent(any());
doReturn(false).when(splitSecondary).isTranslucent(any());
// Re-parent home to split secondary.
- homeStack.reparent(splitSecondary, POSITION_TOP);
+ homeRootTask.reparent(splitSecondary, POSITION_TOP);
// Current tasks should be visible.
assertEquals(TASK_VISIBILITY_VISIBLE, splitPrimary.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE, splitSecondary.getVisibility(null /* starting */));
// Home task should still be visible even though it is a child of another visible task.
- assertEquals(TASK_VISIBILITY_VISIBLE, homeStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, homeRootTask.getVisibility(null /* starting */));
// Add fullscreen translucent task that partially occludes split tasks
- final Task translucentStack = createStandardStackForVisibilityTest(
+ final Task translucentRootTask = createStandardRootTaskForVisibilityTest(
WINDOWING_MODE_FULLSCREEN, true /* translucent */);
// Fullscreen translucent task should be visible
- assertEquals(TASK_VISIBILITY_VISIBLE, translucentStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE,
+ translucentRootTask.getVisibility(null /* starting */));
// Split tasks should be visible behind translucent
assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitPrimary.getVisibility(null /* starting */));
@@ -576,415 +745,400 @@
// Home task should be visible behind translucent since its parent is visible behind
// translucent.
assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- homeStack.getVisibility(null /* starting */));
+ homeRootTask.getVisibility(null /* starting */));
// Hide split-secondary
splitSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
// Home split secondary and home task should be invisible.
assertEquals(TASK_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */));
}
@Test
public void testGetVisibility_FullscreenBehindTranslucent() {
- final Task bottomStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task bottomRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final Task translucentStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task translucentRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- bottomStack.getVisibility(null /* starting */));
+ bottomRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
- translucentStack.getVisibility(null /* starting */));
+ translucentRootTask.getVisibility(null /* starting */));
}
@Test
public void testGetVisibility_FullscreenBehindTranslucentAndOpaque() {
- final Task bottomStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task bottomRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final Task translucentStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task translucentRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final Task opaqueStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task opaqueRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_INVISIBLE,
- translucentStack.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */));
+ translucentRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
}
@Test
public void testGetVisibility_FullscreenBehindOpaqueAndTranslucent() {
- final Task bottomStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task bottomRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final Task opaqueStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task opaqueRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final Task translucentStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task translucentRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- opaqueStack.getVisibility(null /* starting */));
+ opaqueRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
- translucentStack.getVisibility(null /* starting */));
+ translucentRootTask.getVisibility(null /* starting */));
}
@Test
public void testGetVisibility_FullscreenTranslucentBehindTranslucent() {
- final Task bottomTranslucentStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task bottomTranslucentRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final Task translucentStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task translucentRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- bottomTranslucentStack.getVisibility(null /* starting */));
+ bottomTranslucentRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
- translucentStack.getVisibility(null /* starting */));
+ translucentRootTask.getVisibility(null /* starting */));
}
@Test
public void testGetVisibility_FullscreenTranslucentBehindOpaque() {
- final Task bottomTranslucentStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task bottomTranslucentRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final Task opaqueStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task opaqueRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
assertEquals(TASK_VISIBILITY_INVISIBLE,
- bottomTranslucentStack.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */));
+ bottomTranslucentRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
}
@Test
public void testGetVisibility_FullscreenBehindTranslucentAndPip() {
- final Task bottomStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task bottomRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final Task translucentStack =
- createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
+ final Task translucentRootTask =
+ createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- bottomStack.getVisibility(null /* starting */));
+ bottomRootTask.getVisibility(null /* starting */));
assertEquals(TASK_VISIBILITY_VISIBLE,
- translucentStack.getVisibility(null /* starting */));
- // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+ translucentRootTask.getVisibility(null /* starting */));
+ // Add an activity to the pinned root task so it isn't considered empty for visibility
+ // check.
final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
- .setTask(pinnedStack)
+ .setTask(pinnedRootTask)
.build();
- assertEquals(TASK_VISIBILITY_VISIBLE, pinnedStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, pinnedRootTask.getVisibility(null /* starting */));
}
@Test
public void testShouldBeVisible_Finishing() {
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
+ ActivityRecord topRunningHomeActivity = homeRootTask.topRunningActivity();
if (topRunningHomeActivity == null) {
topRunningHomeActivity = new ActivityBuilder(mAtm)
- .setTask(homeStack)
+ .setTask(homeRootTask)
.build();
}
- final Task translucentStack = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- doReturn(true).when(translucentStack).isTranslucent(any());
+ final Task translucentRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ doReturn(true).when(translucentRootTask).isTranslucent(any());
- assertTrue(homeStack.shouldBeVisible(null /* starting */));
- assertTrue(translucentStack.shouldBeVisible(null /* starting */));
+ assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
+ assertTrue(translucentRootTask.shouldBeVisible(null /* starting */));
topRunningHomeActivity.finishing = true;
final ActivityRecord topRunningTranslucentActivity =
- translucentStack.topRunningActivity();
+ translucentRootTask.topRunningActivity();
topRunningTranslucentActivity.finishing = true;
- // Home stack should be visible even there are no running activities.
- assertTrue(homeStack.shouldBeVisible(null /* starting */));
+ // Home root task should be visible even there are no running activities.
+ assertTrue(homeRootTask.shouldBeVisible(null /* starting */));
// Home should be visible if we are starting an activity within it.
- assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */));
- // The translucent stack shouldn't be visible since its activity marked as finishing.
- assertFalse(translucentStack.shouldBeVisible(null /* starting */));
+ assertTrue(homeRootTask.shouldBeVisible(topRunningHomeActivity /* starting */));
+ // The translucent root task shouldn't be visible since its activity marked as finishing.
+ assertFalse(translucentRootTask.shouldBeVisible(null /* starting */));
}
@Test
- public void testShouldBeVisible_FullscreenBehindTranslucentInHomeStack() {
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ public void testShouldBeVisible_FullscreenBehindTranslucentInHomeRootTask() {
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
- .setParentTask(homeStack)
- .setCreateTask(true)
- .build();
+ .setParentTask(homeRootTask)
+ .setCreateTask(true)
+ .build();
final Task task = firstActivity.getTask();
final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
.setTask(task)
.build();
doReturn(false).when(secondActivity).occludesParent();
- homeStack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ homeRootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
false /* preserveWindows */);
assertTrue(firstActivity.shouldBeVisible());
}
@Test
- public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeBehindFullscreen() {
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final Task fullscreenStack = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
-
- doReturn(false).when(homeStack).isTranslucent(any());
- doReturn(false).when(fullscreenStack).isTranslucent(any());
-
- // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
- int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
- assertEquals(fullscreenStack, getRootTaskAbove(homeStack));
- mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
- assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
- }
-
- @Test
- public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final Task fullscreenStack = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea,
+ final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- doReturn(false).when(homeStack).isTranslucent(any());
- doReturn(true).when(fullscreenStack).isTranslucent(any());
+ doReturn(false).when(homeRootTask).isTranslucent(any());
+ doReturn(false).when(fullscreenRootTask).isTranslucent(any());
- // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
- int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
- assertEquals(fullscreenStack, getRootTaskAbove(homeStack));
- mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
- assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
+ // Ensure that we don't move the home root task if it is already behind the top fullscreen
+ // root task.
+ int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
+ assertEquals(fullscreenRootTask, getRootTaskAbove(homeRootTask));
+ mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
+ assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
}
@Test
- public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
- final Task fullscreenStack = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeBehindTranslucent() {
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- doReturn(false).when(homeStack).isTranslucent(any());
- doReturn(false).when(fullscreenStack).isTranslucent(any());
+ doReturn(false).when(homeRootTask).isTranslucent(any());
+ doReturn(true).when(fullscreenRootTask).isTranslucent(any());
- // Ensure we don't move the home stack if it is already on top
- int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
- assertNull(getRootTaskAbove(homeStack));
- mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
- assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
+ // Ensure that we don't move the home root task if it is already behind the top fullscreen
+ // root task.
+ int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
+ assertEquals(fullscreenRootTask, getRootTaskAbove(homeRootTask));
+ mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
+ assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
}
@Test
- public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeOnTop() {
+ final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+
+ doReturn(false).when(homeRootTask).isTranslucent(any());
+ doReturn(false).when(fullscreenRootTask).isTranslucent(any());
+
+ // Ensure we don't move the home root task if it is already on top
+ int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
+ assertNull(getRootTaskAbove(homeRootTask));
+ mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
+ assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
+ }
+
+ @Test
+ public void testMoveHomeRootTaskBehindBottomMostVisible_MoveHomeBehindFullscreen() {
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- doReturn(false).when(homeStack).isTranslucent(any());
- doReturn(false).when(fullscreenStack1).isTranslucent(any());
- doReturn(false).when(fullscreenStack2).isTranslucent(any());
+ doReturn(false).when(homeRootTask).isTranslucent(any());
+ doReturn(false).when(fullscreenRootTask1).isTranslucent(any());
+ doReturn(false).when(fullscreenRootTask2).isTranslucent(any());
- // Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the
- // pinned stack
- assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
- mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
- assertEquals(fullscreenStack2, getRootTaskAbove(homeStack));
+ // Ensure that we move the home root task behind the bottom most fullscreen root task,
+ // ignoring the pinned root task.
+ assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
+ mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
+ assertEquals(fullscreenRootTask2, getRootTaskAbove(homeRootTask));
}
@Test
public void
- testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ testMoveHomeRootTaskBehindBottomMostVisible_MoveHomeBehindFullscreenAndTranslucent() {
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
+ final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- doReturn(false).when(homeStack).isTranslucent(any());
- doReturn(false).when(fullscreenStack1).isTranslucent(any());
- doReturn(true).when(fullscreenStack2).isTranslucent(any());
+ doReturn(false).when(homeRootTask).isTranslucent(any());
+ doReturn(false).when(fullscreenRootTask1).isTranslucent(any());
+ doReturn(true).when(fullscreenRootTask2).isTranslucent(any());
- // Ensure that we move the home stack behind the bottom most non-translucent fullscreen
- // stack
- assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
- mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
- assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
+ // Ensure that we move the home root task behind the bottom most non-translucent fullscreen
+ // root task.
+ assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
+ mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask);
+ assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
}
@Test
- public void testMoveHomeStackBehindStack_BehindHomeStack() {
- final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ public void testMoveHomeRootTaskBehindRootTask_BehindHomeRootTask() {
+ final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- doReturn(false).when(homeStack).isTranslucent(any());
- doReturn(false).when(fullscreenStack1).isTranslucent(any());
- doReturn(false).when(fullscreenStack2).isTranslucent(any());
+ doReturn(false).when(homeRootTask).isTranslucent(any());
+ doReturn(false).when(fullscreenRootTask1).isTranslucent(any());
+ doReturn(false).when(fullscreenRootTask2).isTranslucent(any());
- // Ensure we don't move the home stack behind itself
- int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
- mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, homeStack);
- assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
+ // Ensure we don't move the home root task behind itself
+ int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask);
+ mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, homeRootTask);
+ assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask));
}
@Test
- public void testMoveHomeStackBehindStack() {
- final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task fullscreenStack2 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task fullscreenStack3 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task fullscreenStack4 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ public void testMoveHomeRootTaskBehindRootTask() {
+ final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task fullscreenRootTask3 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task fullscreenRootTask4 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack1);
- assertEquals(fullscreenStack1, getRootTaskAbove(homeStack));
- mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack2);
- assertEquals(fullscreenStack2, getRootTaskAbove(homeStack));
- mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack4);
- assertEquals(fullscreenStack4, getRootTaskAbove(homeStack));
- mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack2);
- assertEquals(fullscreenStack2, getRootTaskAbove(homeStack));
+ mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask1);
+ assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask));
+ mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask2);
+ assertEquals(fullscreenRootTask2, getRootTaskAbove(homeRootTask));
+ mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask4);
+ assertEquals(fullscreenRootTask4, getRootTaskAbove(homeRootTask));
+ mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask2);
+ assertEquals(fullscreenRootTask2, getRootTaskAbove(homeRootTask));
}
@Test
public void testSetAlwaysOnTop() {
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- assertEquals(pinnedStack, getRootTaskAbove(homeStack));
+ assertEquals(pinnedRootTask, getRootTaskAbove(homeRootTask));
- final Task alwaysOnTopStack = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- alwaysOnTopStack.setAlwaysOnTop(true);
- assertTrue(alwaysOnTopStack.isAlwaysOnTop());
- // Ensure (non-pinned) always on top stack is put below pinned stack.
- assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack));
+ final Task alwaysOnTopRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ alwaysOnTopRootTask.setAlwaysOnTop(true);
+ assertTrue(alwaysOnTopRootTask.isAlwaysOnTop());
+ // Ensure (non-pinned) always on top root task is put below pinned root task.
+ assertEquals(pinnedRootTask, getRootTaskAbove(alwaysOnTopRootTask));
- final Task nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
+ final Task nonAlwaysOnTopRootTask = createTaskForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- // Ensure non always on top stack is put below always on top stacks.
- assertEquals(alwaysOnTopStack, getRootTaskAbove(nonAlwaysOnTopStack));
+ // Ensure non always on top root task is put below always on top root tasks.
+ assertEquals(alwaysOnTopRootTask, getRootTaskAbove(nonAlwaysOnTopRootTask));
- final Task alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- alwaysOnTopStack2.setAlwaysOnTop(true);
- assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
- // Ensure newly created always on top stack is placed above other all always on top stacks.
- assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack2));
+ final Task alwaysOnTopRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ alwaysOnTopRootTask2.setAlwaysOnTop(true);
+ assertTrue(alwaysOnTopRootTask2.isAlwaysOnTop());
+ // Ensure newly created always on top root task is placed above other all always on top
+ // root tasks.
+ assertEquals(pinnedRootTask, getRootTaskAbove(alwaysOnTopRootTask2));
- alwaysOnTopStack2.setAlwaysOnTop(false);
- // Ensure, when always on top is turned off for a stack, the stack is put just below all
- // other always on top stacks.
- assertEquals(alwaysOnTopStack, getRootTaskAbove(alwaysOnTopStack2));
- alwaysOnTopStack2.setAlwaysOnTop(true);
+ alwaysOnTopRootTask2.setAlwaysOnTop(false);
+ // Ensure, when always on top is turned off for a root task, the root task is put just below
+ // all other always on top root tasks.
+ assertEquals(alwaysOnTopRootTask, getRootTaskAbove(alwaysOnTopRootTask2));
+ alwaysOnTopRootTask2.setAlwaysOnTop(true);
// Ensure always on top state changes properly when windowing mode changes.
- alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertFalse(alwaysOnTopStack2.isAlwaysOnTop());
- assertEquals(alwaysOnTopStack, getRootTaskAbove(alwaysOnTopStack2));
- alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
- assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack2));
+ alwaysOnTopRootTask2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertFalse(alwaysOnTopRootTask2.isAlwaysOnTop());
+ assertEquals(alwaysOnTopRootTask, getRootTaskAbove(alwaysOnTopRootTask2));
+ alwaysOnTopRootTask2.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertTrue(alwaysOnTopRootTask2.isAlwaysOnTop());
+ assertEquals(pinnedRootTask, getRootTaskAbove(alwaysOnTopRootTask2));
}
@Test
public void testSplitScreenMoveToFront() {
- final Task splitScreenPrimary = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
- ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final Task splitScreenSecondary = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
- ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final Task assistantStack = createStackForShouldBeVisibleTest(
- mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT,
- true /* onTop */);
+ final Task splitScreenPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task splitScreenSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
doReturn(false).when(splitScreenPrimary).isTranslucent(any());
doReturn(false).when(splitScreenSecondary).isTranslucent(any());
- doReturn(false).when(assistantStack).isTranslucent(any());
+ doReturn(false).when(assistantRootTask).isTranslucent(any());
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
- assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+ assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
splitScreenSecondary.moveToFront("testSplitScreenMoveToFront");
if (isAssistantOnTop()) {
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
- assertTrue(assistantStack.shouldBeVisible(null /* starting */));
+ assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
} else {
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
- assertFalse(assistantStack.shouldBeVisible(null /* starting */));
+ assertFalse(assistantRootTask.shouldBeVisible(null /* starting */));
}
}
- private Task createStandardStackForVisibilityTest(int windowingMode,
+ private Task createStandardRootTaskForVisibilityTest(int windowingMode,
boolean translucent) {
- final Task stack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ final Task rootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- doReturn(translucent).when(stack).isTranslucent(any());
- return stack;
+ doReturn(translucent).when(rootTask).isTranslucent(any());
+ return rootTask;
}
- private Task createStackForShouldBeVisibleTest(
+ private Task createTaskForShouldBeVisibleTest(
TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, boolean onTop) {
- return createStackForShouldBeVisibleTest(taskDisplayArea,
+ return createTaskForShouldBeVisibleTest(taskDisplayArea,
windowingMode, activityType, onTop, false /* twoLevelTask */);
}
@SuppressWarnings("TypeParameterUnusedInFormals")
- private Task createStackForShouldBeVisibleTest(TaskDisplayArea taskDisplayArea,
+ private Task createTaskForShouldBeVisibleTest(TaskDisplayArea taskDisplayArea,
int windowingMode, int activityType, boolean onTop, boolean twoLevelTask) {
final Task task;
if (activityType == ACTIVITY_TYPE_HOME) {
@@ -1157,20 +1311,20 @@
}
@Test
- public void testWontFinishHomeStackImmediately() {
- final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
+ public void testWontFinishHomeRootTaskImmediately() {
+ final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- ActivityRecord activity = homeStack.topRunningActivity();
+ ActivityRecord activity = homeRootTask.topRunningActivity();
if (activity == null) {
activity = new ActivityBuilder(mAtm)
- .setParentTask(homeStack)
+ .setParentTask(homeRootTask)
.setCreateTask(true)
.build();
}
- // Home stack should not be destroyed immediately.
- final ActivityRecord activity1 = finishTopActivity(homeStack);
+ // Home root task should not be destroyed immediately.
+ final ActivityRecord activity1 = finishTopActivity(homeRootTask);
assertEquals(FINISHING, activity1.getState());
}
@@ -1178,30 +1332,28 @@
public void testFinishCurrentActivity() {
// Create 2 activities on a new display.
final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
- final Task stack1 = createStackForShouldBeVisibleTest(
- display.getDefaultTaskDisplayArea(),
+ final Task rootTask1 = createTaskForShouldBeVisibleTest(display.getDefaultTaskDisplayArea(),
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final Task stack2 = createStackForShouldBeVisibleTest(
- display.getDefaultTaskDisplayArea(),
+ final Task rootTask2 = createTaskForShouldBeVisibleTest(display.getDefaultTaskDisplayArea(),
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- // There is still an activity1 in stack1 so the activity2 should be added to finishing list
- // that will be destroyed until idle.
- stack2.getTopNonFinishingActivity().mVisibleRequested = true;
- final ActivityRecord activity2 = finishTopActivity(stack2);
+ // There is still an activity1 in rootTask1 so the activity2 should be added to finishing
+ // list that will be destroyed until idle.
+ rootTask2.getTopNonFinishingActivity().mVisibleRequested = true;
+ final ActivityRecord activity2 = finishTopActivity(rootTask2);
assertEquals(STOPPING, activity2.getState());
assertThat(mSupervisor.mStoppingActivities).contains(activity2);
// The display becomes empty. Since there is no next activity to be idle, the activity
// should be destroyed immediately with updating configuration to restore original state.
- final ActivityRecord activity1 = finishTopActivity(stack1);
+ final ActivityRecord activity1 = finishTopActivity(rootTask1);
assertEquals(DESTROYING, activity1.getState());
verify(mRootWindowContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
eq(display.mDisplayId), anyBoolean(), anyBoolean());
}
- private ActivityRecord finishTopActivity(Task stack) {
- final ActivityRecord activity = stack.topRunningActivity();
+ private ActivityRecord finishTopActivity(Task task) {
+ final ActivityRecord activity = task.topRunningActivity();
assertNotNull(activity);
activity.setState(STOPPED, "finishTopActivity");
activity.makeFinishingLocked();
@@ -1214,24 +1366,24 @@
// When focused activity and keyguard is going away, we should not sleep regardless
// of the display state, but keyguard-going-away should only take effects on default
// display since there is no keyguard on secondary displays (yet).
- verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
+ verifyShouldSleepActivities(true /* focusedRootTask */, true /*keyguardGoingAway*/,
true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
- verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
+ verifyShouldSleepActivities(true /* focusedRootTask */, true /*keyguardGoingAway*/,
true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */);
- // When not the focused stack, defer to display sleeping state.
- verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/,
+ // When not the focused root task, defer to display sleeping state.
+ verifyShouldSleepActivities(false /* focusedRootTask */, true /*keyguardGoingAway*/,
true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
// If keyguard is going away, defer to the display sleeping state.
- verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
+ verifyShouldSleepActivities(true /* focusedRootTask */, false /*keyguardGoingAway*/,
true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
- verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
+ verifyShouldSleepActivities(true /* focusedRootTask */, false /*keyguardGoingAway*/,
false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
}
@Test
- public void testStackOrderChangedOnRemoveStack() {
+ public void testRootTaskOrderChangedOnRemoveRootTask() {
final Task task = new TaskBuilder(mSupervisor).build();
RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener();
mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener);
@@ -1244,7 +1396,7 @@
}
@Test
- public void testStackOrderChangedOnAddPositionStack() {
+ public void testRootTaskOrderChangedOnAddPositionRootTask() {
final Task task = new TaskBuilder(mSupervisor).build();
mDefaultTaskDisplayArea.removeRootTask(task);
@@ -1260,14 +1412,14 @@
}
@Test
- public void testStackOrderChangedOnPositionStack() {
+ public void testRootTaskOrderChangedOnPositionRootTask() {
RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener();
try {
- final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener);
- mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenStack1,
+ mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenRootTask1,
false /*includingParents*/);
} finally {
mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener);
@@ -1443,7 +1595,7 @@
com.android.internal.R.bool.config_assistantOnTopOfDream);
}
- private void verifyShouldSleepActivities(boolean focusedStack,
+ private void verifyShouldSleepActivities(boolean focusedRootTask,
boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay,
boolean expected) {
final Task task = new TaskBuilder(mSupervisor).build();
@@ -1454,13 +1606,13 @@
task.mDisplayContent = display;
doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
doReturn(displaySleeping).when(display).isSleeping();
- doReturn(focusedStack).when(task).isFocusedRootTaskOnDisplay();
+ doReturn(focusedRootTask).when(task).isFocusedRootTaskOnDisplay();
assertEquals(expected, task.shouldSleepActivities());
}
private static class RootTaskOrderChangedListener
- implements OnRootTaskOrderChangedListener {
+ implements TaskDisplayArea.OnRootTaskOrderChangedListener {
public boolean mChanged = false;
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 1b114c1..20bced2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -16,42 +16,93 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.TYPE_VIRTUAL;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.Task.ActivityState.FINISHING;
import static com.android.server.wm.Task.ActivityState.PAUSED;
import static com.android.server.wm.Task.ActivityState.PAUSING;
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.Task.ActivityState.STOPPING;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+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.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.refEq;
+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 android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
+import android.util.Pair;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
+import com.android.internal.app.ResolverActivity;
+
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
/**
- * Tests for RootWindowContainer.
+ * Tests for {@link RootWindowContainer}.
*
* Build/Install/Run:
* atest WmTests:RootWindowContainerTests
*/
-@SmallTest
+@MediumTest
@Presubmit
@RunWith(WindowTestRunner.class)
public class RootWindowContainerTests extends WindowTestsBase {
+ @Before
+ public void setUp() throws Exception {
+ doNothing().when(mAtm).updateSleepIfNeededLocked();
+ }
+
@Test
public void testUpdateDefaultDisplayWindowingModeOnSettingsRetrieved() {
assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
@@ -166,5 +217,860 @@
assertFalse(task.hasChild());
assertFalse(wpc.hasActivities());
}
+
+ /**
+ * This test ensures that we do not try to restore a task based off an invalid task id. We
+ * should expect {@code null} to be returned in this case.
+ */
+ @Test
+ public void testRestoringInvalidTask() {
+ mRootWindowContainer.getDefaultDisplay().removeAllTasks();
+ Task task = mRootWindowContainer.anyTaskForId(0 /*taskId*/,
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
+ assertNull(task);
+ }
+
+ /**
+ * This test ensures that an existing task in the pinned root task is moved to the fullscreen
+ * activity root task when a new task is added.
+ */
+ @Test
+ public void testReplacingTaskInPinnedRootTask() {
+ Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+ .setTask(fullscreenTask).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+ .setTask(fullscreenTask).build();
+
+ fullscreenTask.moveToFront("testReplacingTaskInPinnedRootTask");
+
+ // Ensure full screen root task has both tasks.
+ ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity);
+
+ // Move first activity to pinned root task.
+ mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove");
+
+ final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
+ Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
+ // Ensure a task has moved over.
+ ensureTaskPlacement(pinnedRootTask, firstActivity);
+ ensureTaskPlacement(fullscreenTask, secondActivity);
+
+ // Move second activity to pinned root task.
+ mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove");
+
+ // Need to get root tasks again as a new instance might have been created.
+ pinnedRootTask = taskDisplayArea.getRootPinnedTask();
+ fullscreenTask = taskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD);
+ // Ensure root tasks have swapped tasks.
+ ensureTaskPlacement(pinnedRootTask, secondActivity);
+ ensureTaskPlacement(fullscreenTask, firstActivity);
+ }
+
+ @Test
+ public void testMovingBottomMostRootTaskActivityToPinnedRootTask() {
+ final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+ .setTask(fullscreenTask).build();
+ final Task task = firstActivity.getTask();
+
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+ .setTask(fullscreenTask).build();
+
+ fullscreenTask.moveTaskToBack(task);
+
+ // Ensure full screen task has both tasks.
+ ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity);
+ assertEquals(task.getTopMostActivity(), secondActivity);
+ firstActivity.setState(STOPPED, "testMovingBottomMostRootTaskActivityToPinnedRootTask");
+
+
+ // Move first activity to pinned root task.
+ mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove");
+
+ assertTrue(firstActivity.mRequestForceTransition);
+ }
+
+ private static void ensureTaskPlacement(Task task, ActivityRecord... activities) {
+ final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();
+
+ task.forAllActivities((Consumer<ActivityRecord>) taskActivities::add, false);
+
+ assertEquals("Expecting " + Arrays.deepToString(activities) + " got " + taskActivities,
+ taskActivities.size(), activities != null ? activities.length : 0);
+
+ if (activities == null) {
+ return;
+ }
+
+ for (ActivityRecord activity : activities) {
+ assertTrue(taskActivities.contains(activity));
+ }
+ }
+
+ @Test
+ public void testApplySleepTokens() {
+ final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+ final KeyguardController keyguard = mSupervisor.getKeyguardController();
+ final Task task = new TaskBuilder(mSupervisor)
+ .setDisplay(display)
+ .setOnTop(false)
+ .build();
+
+ // Make sure we wake and resume in the case the display is turning on and the keyguard is
+ // not showing.
+ verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/,
+ false /* displayShouldSleep */, true /* isFocusedTask */,
+ false /* keyguardShowing */, true /* expectWakeFromSleep */,
+ true /* expectResumeTopActivity */);
+
+ // Make sure we wake and don't resume when the display is turning on and the keyguard is
+ // showing.
+ verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/,
+ false /* displayShouldSleep */, true /* isFocusedTask */,
+ true /* keyguardShowing */, true /* expectWakeFromSleep */,
+ false /* expectResumeTopActivity */);
+
+ // Make sure we wake and don't resume when the display is turning on and the keyguard is
+ // not showing as unfocused.
+ verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/,
+ false /* displayShouldSleep */, false /* isFocusedTask */,
+ false /* keyguardShowing */, true /* expectWakeFromSleep */,
+ false /* expectResumeTopActivity */);
+
+ // Should not do anything if the display state hasn't changed.
+ verifySleepTokenBehavior(display, keyguard, task, false /*displaySleeping*/,
+ false /* displayShouldSleep */, true /* isFocusedTask */,
+ false /* keyguardShowing */, false /* expectWakeFromSleep */,
+ false /* expectResumeTopActivity */);
+ }
+
+ private void verifySleepTokenBehavior(DisplayContent display, KeyguardController keyguard,
+ Task task, boolean displaySleeping, boolean displayShouldSleep,
+ boolean isFocusedTask, boolean keyguardShowing, boolean expectWakeFromSleep,
+ boolean expectResumeTopActivity) {
+ reset(task);
+
+ doReturn(displayShouldSleep).when(display).shouldSleep();
+ doReturn(displaySleeping).when(display).isSleeping();
+ doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
+
+ doReturn(isFocusedTask).when(task).isFocusedRootTaskOnDisplay();
+ doReturn(isFocusedTask ? task : null).when(display).getFocusedRootTask();
+ TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea();
+ doReturn(isFocusedTask ? task : null).when(defaultTaskDisplayArea).getFocusedRootTask();
+ mRootWindowContainer.applySleepTokens(true);
+ verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+ verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
+ null /* target */, null /* targetOptions */);
+ }
+
+ @Test
+ public void testAwakeFromSleepingWithAppConfiguration() {
+ final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ activity.moveFocusableActivityToTop("test");
+ assertTrue(activity.getRootTask().isFocusedRootTaskOnDisplay());
+ ActivityRecordTests.setRotatedScreenOrientationSilently(activity);
+
+ final Configuration rotatedConfig = new Configuration();
+ display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation()
+ .rotationForOrientation(activity.getOrientation(), display.getRotation()));
+ assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation);
+ // Assume the activity was shown in different orientation. For example, the top activity is
+ // landscape and the portrait lockscreen is shown.
+ activity.setLastReportedConfiguration(
+ new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig));
+ activity.setState(Task.ActivityState.STOPPED, "sleep");
+
+ display.setIsSleeping(true);
+ doReturn(false).when(display).shouldSleep();
+ // Allow to resume when awaking.
+ setBooted(mAtm);
+ mRootWindowContainer.applySleepTokens(true);
+
+ // The display orientation should be changed by the activity so there is no relaunch.
+ verify(activity, never()).relaunchActivityLocked(anyBoolean());
+ assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation);
+ }
+
+ /**
+ * Verifies that removal of activity with task and root task is done correctly.
+ */
+ @Test
+ public void testRemovingRootTaskOnAppCrash() {
+ final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
+ .getDefaultTaskDisplayArea();
+ final int originalRootTaskCount = defaultTaskDisplayArea.getRootTaskCount();
+ final Task rootTask = defaultTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(rootTask).build();
+
+ assertEquals(originalRootTaskCount + 1, defaultTaskDisplayArea.getRootTaskCount());
+
+ // Let's pretend that the app has crashed.
+ firstActivity.app.setThread(null);
+ mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
+
+ // Verify that the root task was removed.
+ assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount());
+ }
+
+ /**
+ * Verifies that removal of activities with task and root task is done correctly when there are
+ * several task display areas.
+ */
+ @Test
+ public void testRemovingRootTaskOnAppCrash_multipleDisplayAreas() {
+ final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
+ .getDefaultTaskDisplayArea();
+ final int originalRootTaskCount = defaultTaskDisplayArea.getRootTaskCount();
+ final Task rootTask = defaultTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(rootTask).build();
+ assertEquals(originalRootTaskCount + 1, defaultTaskDisplayArea.getRootTaskCount());
+
+ final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent();
+ final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+ dc, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST);
+ final Task secondRootTask = secondTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ new ActivityBuilder(mAtm).setTask(secondRootTask).setUseProcess(firstActivity.app).build();
+ assertEquals(1, secondTaskDisplayArea.getRootTaskCount());
+
+ // Let's pretend that the app has crashed.
+ firstActivity.app.setThread(null);
+ mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
+
+ // Verify that the root tasks were removed.
+ assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount());
+ assertEquals(0, secondTaskDisplayArea.getRootTaskCount());
+ }
+
+ @Test
+ public void testFocusability() {
+ final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
+ .getDefaultTaskDisplayArea();
+ final Task task = defaultTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
+
+ // Created tasks are focusable by default.
+ assertTrue(task.isTopActivityFocusable());
+ assertTrue(activity.isFocusable());
+
+ // If the task is made unfocusable, its activities should inherit that.
+ task.setFocusable(false);
+ assertFalse(task.isTopActivityFocusable());
+ assertFalse(activity.isFocusable());
+
+ final Task pinnedTask = defaultTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
+ .setTask(pinnedTask).build();
+
+ // We should not be focusable when in pinned mode
+ assertFalse(pinnedTask.isTopActivityFocusable());
+ assertFalse(pinnedActivity.isFocusable());
+
+ // Add flag forcing focusability.
+ pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
+
+ // Task with FLAG_ALWAYS_FOCUSABLE should be focusable.
+ assertTrue(pinnedTask.isTopActivityFocusable());
+ assertTrue(pinnedActivity.isFocusable());
+ }
+
+ /**
+ * Verify that split-screen primary root task will be chosen if activity is launched that
+ * targets split-screen secondary, but a matching existing instance is found on top of
+ * split-screen primary root task.
+ */
+ @Test
+ public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
+ // Create primary split-screen root task with a task and an activity.
+ final Task primaryRootTask = mRootWindowContainer.getDefaultTaskDisplayArea()
+ .createRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ final Task task = new TaskBuilder(mSupervisor).setParentTask(primaryRootTask).build();
+ final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build();
+
+ // Find a launch root task for the top activity in split-screen primary, while requesting
+ // split-screen secondary.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ final Task result =
+ mRootWindowContainer.getLaunchRootTask(r, options, task, true /* onTop */);
+
+ // Assert that the primary root task is returned.
+ assertEquals(primaryRootTask, result);
+ }
+
+ /**
+ * Verify that home root task would be moved to front when the top activity is Recents.
+ */
+ @Test
+ public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
+ // Create root task/task on default display.
+ final Task targetRootTask = new TaskBuilder(mSupervisor)
+ .setCreateActivity(true)
+ .setOnTop(false)
+ .build();
+ final Task targetTask = targetRootTask.getBottomMostTask();
+
+ // Create Recents on top of the display.
+ final Task rootTask = new TaskBuilder(mSupervisor)
+ .setCreateActivity(true)
+ .setActivityType(ACTIVITY_TYPE_RECENTS)
+ .build();
+
+ final String reason = "findTaskToMoveToFront";
+ mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+ false);
+
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ verify(taskDisplayArea).moveHomeRootTaskToFront(contains(reason));
+ }
+
+ /**
+ * Verify that home root task won't be moved to front if the top activity on other display is
+ * Recents.
+ */
+ @Test
+ public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
+ // Create tasks on default display.
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ final Task targetRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetRootTask).build();
+
+ // Create Recents on secondary display.
+ final TestDisplayContent secondDisplay = addNewDisplayContentAt(
+ DisplayContent.POSITION_TOP);
+ final Task rootTask = secondDisplay.getDefaultTaskDisplayArea()
+ .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ new ActivityBuilder(mAtm).setTask(rootTask).build();
+
+ final String reason = "findTaskToMoveToFront";
+ mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+ false);
+
+ verify(taskDisplayArea, never()).moveHomeRootTaskToFront(contains(reason));
+ }
+
+ /**
+ * Verify if a root task is not at the topmost position, it should be able to resume its
+ * activity if the root task is the top focused.
+ */
+ @Test
+ public void testResumeActivityWhenNonTopmostRootTaskIsTopFocused() {
+ // Create a root task at bottom.
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
+ taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
+
+ // Assume the task is not at the topmost position (e.g. behind always-on-top root tasks)
+ // but it is the current top focused task.
+ assertFalse(rootTask.isTopRootTaskInDisplayArea());
+ doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
+
+ // Use the task as target to resume.
+ mRootWindowContainer.resumeFocusedTasksTopActivities(
+ rootTask, activity, null /* targetOptions */);
+
+ // Verify the target task should resume its activity.
+ verify(rootTask, times(1)).resumeTopActivityUncheckedLocked(
+ eq(activity), eq(null /* targetOptions */));
+ }
+
+ /**
+ * Verify that home activity will be started on a display even if another display has a
+ * focusable activity.
+ */
+ @Test
+ public void testResumeFocusedRootTasksStartsHomeActivity_NoActivities() {
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ taskDisplayArea.getRootHomeTask().removeIfPossible();
+ taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+
+ doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
+
+ mAtm.setBooted(true);
+
+ // Trigger resume on all displays
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+
+ // Verify that home activity was started on the default display
+ verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea));
+ }
+
+ /**
+ * Verify that home activity will be started on a display even if another display has a
+ * focusable activity.
+ */
+ @Test
+ public void testResumeFocusedRootTasksStartsHomeActivity_ActivityOnSecondaryScreen() {
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ taskDisplayArea.getRootHomeTask().removeIfPossible();
+ taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+
+ // Create an activity on secondary display.
+ final TestDisplayContent secondDisplay = addNewDisplayContentAt(
+ DisplayContent.POSITION_TOP);
+ final Task rootTask = secondDisplay.getDefaultTaskDisplayArea().createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ new ActivityBuilder(mAtm).setTask(rootTask).build();
+
+ doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
+
+ mAtm.setBooted(true);
+
+ // Trigger resume on all displays
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+
+ // Verify that home activity was started on the default display
+ verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea));
+ }
+
+ /**
+ * Verify that a lingering transition is being executed in case the activity to be resumed is
+ * already resumed
+ */
+ @Test
+ public void testResumeActivityLingeringTransition() {
+ // Create a root task at top.
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(rootTask).setOnTop(true).build();
+ activity.setState(Task.ActivityState.RESUMED, "test");
+
+ // Assume the task is at the topmost position
+ assertTrue(rootTask.isTopRootTaskInDisplayArea());
+
+ // Use the task as target to resume.
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+
+ // Verify the lingering app transition is being executed because it's already resumed
+ verify(rootTask, times(1)).executeAppTransition(any());
+ }
+
+ @Test
+ public void testResumeActivityLingeringTransition_notExecuted() {
+ // Create a root task at bottom.
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(rootTask).setOnTop(true).build();
+ activity.setState(Task.ActivityState.RESUMED, "test");
+ taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
+
+ // Assume the task is at the topmost position
+ assertFalse(rootTask.isTopRootTaskInDisplayArea());
+ doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
+
+ // Use the task as target to resume.
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+
+ // Verify the lingering app transition is being executed because it's already resumed
+ verify(rootTask, never()).executeAppTransition(any());
+ }
+
+ /**
+ * Tests that home activities can be started on the displays that supports system decorations.
+ */
+ @Test
+ public void testStartHomeOnAllDisplays() {
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ mockResolveSecondaryHomeActivity();
+
+ // Create secondary displays.
+ final TestDisplayContent secondDisplay =
+ new TestDisplayContent.Builder(mAtm, 1000, 1500)
+ .setSystemDecorations(true).build();
+
+ doReturn(true).when(mRootWindowContainer)
+ .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+ anyBoolean());
+
+ mRootWindowContainer.startHomeOnAllDisplays(0, "testStartHome");
+
+ assertTrue(mRootWindowContainer.getDefaultDisplay().getTopRootTask().isActivityTypeHome());
+ assertNotNull(secondDisplay.getTopRootTask());
+ assertTrue(secondDisplay.getTopRootTask().isActivityTypeHome());
+ }
+
+ /**
+ * Tests that home activities won't be started before booting when display added.
+ */
+ @Test
+ public void testNotStartHomeBeforeBoot() {
+ final int displayId = 1;
+ final boolean isBooting = mAtm.mAmInternal.isBooting();
+ final boolean isBooted = mAtm.mAmInternal.isBooted();
+ try {
+ mAtm.mAmInternal.setBooting(false);
+ mAtm.mAmInternal.setBooted(false);
+ mRootWindowContainer.onDisplayAdded(displayId);
+ verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
+ } finally {
+ mAtm.mAmInternal.setBooting(isBooting);
+ mAtm.mAmInternal.setBooted(isBooted);
+ }
+ }
+
+ /**
+ * Tests whether home can be started if being instrumented.
+ */
+ @Test
+ public void testCanStartHomeWhenInstrumented() {
+ final ActivityInfo info = new ActivityInfo();
+ info.applicationInfo = new ApplicationInfo();
+ final WindowProcessController app = mock(WindowProcessController.class);
+ doReturn(app).when(mAtm).getProcessController(any(), anyInt());
+
+ // Can not start home if we don't want to start home while home is being instrumented.
+ doReturn(true).when(app).isInstrumenting();
+ final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
+ .getDefaultTaskDisplayArea();
+ assertFalse(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
+ false /* allowInstrumenting*/));
+
+ // Can start home for other cases.
+ assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
+ true /* allowInstrumenting*/));
+
+ doReturn(false).when(app).isInstrumenting();
+ assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
+ false /* allowInstrumenting*/));
+ assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea,
+ true /* allowInstrumenting*/));
+ }
+
+ /**
+ * Tests that secondary home activity should not be resolved if device is still locked.
+ */
+ @Test
+ public void testStartSecondaryHomeOnDisplayWithUserKeyLocked() {
+ // Create secondary displays.
+ final TestDisplayContent secondDisplay =
+ new TestDisplayContent.Builder(mAtm, 1000, 1500)
+ .setSystemDecorations(true).build();
+
+ // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false.
+ final int currentUser = mRootWindowContainer.mCurrentUser;
+ mRootWindowContainer.mCurrentUser = -1;
+
+ mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome",
+ secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */);
+
+ try {
+ verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any());
+ } finally {
+ mRootWindowContainer.mCurrentUser = currentUser;
+ }
+ }
+
+ /**
+ * Tests that secondary home activity should not be resolved if display does not support system
+ * decorations.
+ */
+ @Test
+ public void testStartSecondaryHomeOnDisplayWithoutSysDecorations() {
+ // Create secondary displays.
+ final TestDisplayContent secondDisplay =
+ new TestDisplayContent.Builder(mAtm, 1000, 1500)
+ .setSystemDecorations(false).build();
+
+ mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome",
+ secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */);
+
+ verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any());
+ }
+
+ /**
+ * Tests that when starting {@link #ResolverActivity} for home, it should use the standard
+ * activity type (in a new root task) so the order of back stack won't be broken.
+ */
+ @Test
+ public void testStartResolverActivityForHome() {
+ final ActivityInfo info = new ActivityInfo();
+ info.applicationInfo = new ApplicationInfo();
+ info.applicationInfo.packageName = "android";
+ info.name = ResolverActivity.class.getName();
+ doReturn(info).when(mRootWindowContainer).resolveHomeActivity(anyInt(), any());
+
+ mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
+ final ActivityRecord resolverActivity = mRootWindowContainer.topRunningActivity();
+
+ assertEquals(info, resolverActivity.info);
+ assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getRootTask().getActivityType());
+ }
+
+ /**
+ * Tests that secondary home should be selected if primary home not set.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() {
+ // Setup: primary home not set.
+ final Intent primaryHomeIntent = mAtm.getHomeIntent();
+ final ActivityInfo aInfoPrimary = new ActivityInfo();
+ aInfoPrimary.name = ResolverActivity.class.getName();
+ doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ refEq(primaryHomeIntent));
+ // Setup: set secondary home.
+ mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
+
+ // Run the test.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ }
+
+ /**
+ * Tests that the default secondary home activity is always picked when it is in forced by
+ * config_useSystemProvidedLauncherForSecondary.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityForced() {
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // SetUp: set secondary home and force it.
+ mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */);
+ final Intent secondaryHomeIntent =
+ mAtm.getSecondaryHomeIntent(null /* preferredPackage */);
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ resolveInfo.activityInfo = aInfoSecondary;
+ resolutions.add(resolveInfo);
+ doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(),
+ refEq(secondaryHomeIntent));
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+ anyBoolean());
+
+ // Run the test.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ }
+
+ /**
+ * Tests that secondary home should be selected if primary home not support secondary displays
+ * or there is no matched activity in the same package as selected primary home.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() {
+ // Setup: there is no matched activity in the same package as selected primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
+ // Setup: set secondary home.
+ mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
+
+ // Run the test.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ }
+ /**
+ * Tests that primary home activity should be selected if it already support secondary displays.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() {
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // SetUp: put primary home info on 2nd item
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ final ResolveInfo infoFake1 = new ResolveInfo();
+ infoFake1.activityInfo = new ActivityInfo();
+ infoFake1.activityInfo.name = "fakeActivity1";
+ infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+ infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+ final ResolveInfo infoFake2 = new ResolveInfo();
+ final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */);
+ infoFake2.activityInfo = aInfoPrimary;
+ resolutions.add(infoFake1);
+ resolutions.add(infoFake2);
+ doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
+
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+ anyBoolean());
+
+ // Run the test.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
+ assertEquals(aInfoPrimary.name, resolvedInfo.first.name);
+ assertEquals(aInfoPrimary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ }
+
+ /**
+ * Tests that the first one that matches should be selected if there are multiple activities.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // Setup: prepare two eligible activity info.
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ final ResolveInfo infoFake1 = new ResolveInfo();
+ infoFake1.activityInfo = new ActivityInfo();
+ infoFake1.activityInfo.name = "fakeActivity1";
+ infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
+ infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
+ final ResolveInfo infoFake2 = new ResolveInfo();
+ infoFake2.activityInfo = new ActivityInfo();
+ infoFake2.activityInfo.name = "fakeActivity2";
+ infoFake2.activityInfo.applicationInfo = new ApplicationInfo();
+ infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2";
+ resolutions.add(infoFake1);
+ resolutions.add(infoFake2);
+ doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
+
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
+ anyBoolean());
+
+ // Use the first one of matched activities in the same package as selected primary home.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class));
+
+ assertEquals(infoFake1.activityInfo.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name);
+ }
+
+ /**
+ * Test that {@link RootWindowContainer#getLaunchRootTask} with the real caller id will get the
+ * expected root task when requesting the activity launch on the secondary display.
+ */
+ @Test
+ public void testGetLaunchRootTaskWithRealCallerId() {
+ // Create a non-system owned virtual display.
+ final TestDisplayContent secondaryDisplay =
+ new TestDisplayContent.Builder(mAtm, 1000, 1500)
+ .setType(TYPE_VIRTUAL).setOwnerUid(100).build();
+
+ // Create an activity with specify the original launch pid / uid.
+ final ActivityRecord r = new ActivityBuilder(mAtm).setLaunchedFromPid(200)
+ .setLaunchedFromUid(200).build();
+
+ // Simulate ActivityStarter to find a launch root task for requesting the activity to launch
+ // on the secondary display with realCallerId.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(secondaryDisplay.mDisplayId);
+ options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId,
+ 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info);
+ final Task result = mRootWindowContainer.getLaunchRootTask(r, options,
+ null /* task */, true /* onTop */, null, 300 /* test realCallerPid */,
+ 300 /* test realCallerUid */);
+
+ // Assert that the root task is returned as expected.
+ assertNotNull(result);
+ assertEquals("The display ID of the root task should same as secondary display ",
+ secondaryDisplay.mDisplayId, result.getDisplayId());
+ }
+
+ @Test
+ public void testGetValidLaunchRootTaskOnDisplayWithCandidateRootTask() {
+ // Create a root task with an activity on secondary display.
+ final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300,
+ 600).build();
+ final Task task = new TaskBuilder(mSupervisor)
+ .setDisplay(secondaryDisplay).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
+
+ // Make sure the root task is valid and can be reused on default display.
+ final Task rootTask = mRootWindowContainer.getValidLaunchRootTaskInTaskDisplayArea(
+ mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task,
+ null /* options */, null /* launchParams */);
+ assertEquals(task, rootTask);
+ }
+
+ @Test
+ public void testSwitchUser_missingHomeRootTask() {
+ final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ doReturn(fullscreenTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
+
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ Task rootHomeTask = taskDisplayArea.getRootHomeTask();
+ if (rootHomeTask != null) {
+ rootHomeTask.removeImmediately();
+ }
+ assertNull(taskDisplayArea.getRootHomeTask());
+
+ int currentUser = mRootWindowContainer.mCurrentUser;
+ int otherUser = currentUser + 1;
+
+ mRootWindowContainer.switchUser(otherUser, null);
+
+ assertNotNull(taskDisplayArea.getRootHomeTask());
+ assertEquals(taskDisplayArea.getTopRootTask(), taskDisplayArea.getRootHomeTask());
+ }
+
+ /**
+ * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
+ * info for test cases.
+ *
+ * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use
+ * secondary home intent.
+ * @param forceSystemProvided Indicate to force using system provided home activity.
+ */
+ private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) {
+ ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome);
+ Intent targetIntent;
+ if (primaryHome) {
+ targetIntent = mAtm.getHomeIntent();
+ } else {
+ Resources resources = mContext.getResources();
+ spyOn(resources);
+ doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString(
+ com.android.internal.R.string.config_secondaryHomePackage);
+ doReturn(forceSystemProvided).when(resources).getBoolean(
+ com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
+ targetIntent = mAtm.getSecondaryHomeIntent(null /* preferredPackage */);
+ }
+ doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ refEq(targetIntent));
+ }
+
+ /**
+ * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent
+ * activity info for test cases.
+ */
+ private void mockResolveSecondaryHomeActivity() {
+ final Intent secondaryHomeIntent = mAtm
+ .getSecondaryHomeIntent(null /* preferredPackage */);
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false);
+ doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer)
+ .resolveSecondaryHomeActivity(anyInt(), any());
+ }
+
+ private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) {
+ final ActivityInfo aInfo = new ActivityInfo();
+ aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity";
+ aInfo.applicationInfo = new ApplicationInfo();
+ aInfo.applicationInfo.packageName =
+ primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage";
+ return aInfo;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index eba5634..92d4ede 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -28,6 +28,7 @@
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 static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -35,13 +36,16 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -56,8 +60,6 @@
import com.android.server.wm.LaunchParamsController.LaunchParams;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -72,35 +74,17 @@
@RunWith(WindowTestRunner.class)
public class TaskDisplayAreaTests extends WindowTestsBase {
- private Task mPinnedTask;
-
- @Before
- public void setUp() throws Exception {
- mPinnedTask = createTaskStackOnDisplay(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
- // Stack should contain visible app window to be considered visible.
- assertFalse(mPinnedTask.isVisible());
- final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent);
- mPinnedTask.addChild(pinnedApp, 0 /* addPos */);
- assertTrue(mPinnedTask.isVisible());
- }
-
- @After
- public void tearDown() throws Exception {
- mPinnedTask.removeImmediately();
- }
-
@Test
public void getOrCreateLaunchRootRespectsResolvedWindowingMode() {
- final Task rootTask = createTaskStackOnDisplay(
- WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task rootTask = createTask(
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
rootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
taskDisplayArea.setLaunchRootTask(
rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD});
- final Task candidateRootTask = createTaskStackOnDisplay(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task candidateRootTask = createTask(
+ mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
final LaunchParams launchParams = new LaunchParams();
launchParams.mWindowingMode = WINDOWING_MODE_FREEFORM;
@@ -113,15 +97,15 @@
@Test
public void getOrCreateLaunchRootUsesActivityOptionsWindowingMode() {
- final Task rootTask = createTaskStackOnDisplay(
- WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task rootTask = createTask(
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
rootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
taskDisplayArea.setLaunchRootTask(
rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD});
- final Task candidateRootTask = createTaskStackOnDisplay(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task candidateRootTask = createTask(
+ mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -134,9 +118,8 @@
@Test
public void testActivityWithZBoost_taskDisplayAreaDoesNotMoveUp() {
- final Task stack = createTaskStackOnDisplay(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
task.addChild(activity, 0 /* addPos */);
final TaskDisplayArea taskDisplayArea = activity.getDisplayArea();
@@ -152,87 +135,103 @@
}
@Test
- public void testStackPositionChildAt() {
- // Test that always-on-top stack can't be moved to position other than top.
- final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
- final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
+ public void testRootTaskPositionChildAt() {
+ Task pinnedTask = createTask(
+ mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+ // Root task should contain visible app window to be considered visible.
+ assertFalse(pinnedTask.isVisible());
+ final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent);
+ pinnedTask.addChild(pinnedApp, 0 /* addPos */);
+ assertTrue(pinnedTask.isVisible());
- final WindowContainer taskStackContainer = stack1.getParent();
+ // Test that always-on-top root task can't be moved to position other than top.
+ final Task rootTask1 = createTask(mDisplayContent);
+ final Task rootTask2 = createTask(mDisplayContent);
- final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1);
- final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2);
- final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask);
- assertThat(pinnedStackPos).isGreaterThan(stack2Pos);
- assertThat(stack2Pos).isGreaterThan(stack1Pos);
+ final WindowContainer taskContainer = rootTask1.getParent();
- taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedTask, false);
- assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
- assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
- assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
+ final int rootTask1Pos = taskContainer.mChildren.indexOf(rootTask1);
+ final int rootTask2Pos = taskContainer.mChildren.indexOf(rootTask2);
+ final int pinnedTaskPos = taskContainer.mChildren.indexOf(pinnedTask);
+ assertThat(pinnedTaskPos).isGreaterThan(rootTask2Pos);
+ assertThat(rootTask2Pos).isGreaterThan(rootTask1Pos);
- taskStackContainer.positionChildAt(1, mPinnedTask, false);
- assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
- assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
- assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
+ taskContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, pinnedTask, false);
+ assertEquals(taskContainer.mChildren.get(rootTask1Pos), rootTask1);
+ assertEquals(taskContainer.mChildren.get(rootTask2Pos), rootTask2);
+ assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask);
+
+ taskContainer.positionChildAt(1, pinnedTask, false);
+ assertEquals(taskContainer.mChildren.get(rootTask1Pos), rootTask1);
+ assertEquals(taskContainer.mChildren.get(rootTask2Pos), rootTask2);
+ assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask);
}
@Test
- public void testStackPositionBelowPinnedStack() {
- // Test that no stack can be above pinned stack.
- final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
+ public void testRootTaskPositionBelowPinnedRootTask() {
+ Task pinnedTask = createTask(
+ mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+ // Root task should contain visible app window to be considered visible.
+ assertFalse(pinnedTask.isVisible());
+ final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent);
+ pinnedTask.addChild(pinnedApp, 0 /* addPos */);
+ assertTrue(pinnedTask.isVisible());
- final WindowContainer taskStackContainer = stack1.getParent();
+ // Test that no root task can be above pinned root task.
+ final Task rootTask1 = createTask(mDisplayContent);
- final int stackPos = taskStackContainer.mChildren.indexOf(stack1);
- final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask);
- assertThat(pinnedStackPos).isGreaterThan(stackPos);
+ final WindowContainer taskContainer = rootTask1.getParent();
- taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false);
- assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
- assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
+ final int rootTaskPos = taskContainer.mChildren.indexOf(rootTask1);
+ final int pinnedTaskPos = taskContainer.mChildren.indexOf(pinnedTask);
+ assertThat(pinnedTaskPos).isGreaterThan(rootTaskPos);
- taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false);
- assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
- assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
+ taskContainer.positionChildAt(WindowContainer.POSITION_TOP, rootTask1, false);
+ assertEquals(taskContainer.mChildren.get(rootTaskPos), rootTask1);
+ assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask);
+
+ taskContainer.positionChildAt(taskContainer.mChildren.size() - 1, rootTask1, false);
+ assertEquals(taskContainer.mChildren.get(rootTaskPos), rootTask1);
+ assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask);
}
@Test
- public void testDisplayPositionWithPinnedStack() {
- // Make sure the display is trusted display which capable to move the stack to top.
+ public void testDisplayPositionWithPinnedRootTask() {
+ // Make sure the display is trusted display which capable to move the root task to top.
spyOn(mDisplayContent);
doReturn(true).when(mDisplayContent).isTrusted();
- // Allow child stack to move to top.
+ // Allow child root task to move to top.
mDisplayContent.mDontMoveToTop = false;
- // The display contains pinned stack that was added in {@link #setUp}.
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
+ // The display contains pinned root task that was added in {@link #setUp}.
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
// Add another display at top.
mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
false /* includingParents */);
// Move the task of {@code mDisplayContent} to top.
- stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
- final int indexOfDisplayWithPinnedStack = mWm.mRoot.mChildren.indexOf(mDisplayContent);
+ rootTask.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+ final int indexOfDisplayWithPinnedRootTask = mWm.mRoot.mChildren.indexOf(mDisplayContent);
assertEquals("The testing DisplayContent should be moved to top with task",
- mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack);
+ mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedRootTask);
}
@Test
public void testMovingChildTaskOnTop() {
- // Make sure the display is trusted display which capable to move the stack to top.
+ // Make sure the display is trusted display which capable to move the root task to top.
spyOn(mDisplayContent);
doReturn(true).when(mDisplayContent).isTrusted();
- // Allow child stack to move to top.
+ // Allow child root task to move to top.
mDisplayContent.mDontMoveToTop = false;
- // The display contains pinned stack that was added in {@link #setUp}.
- Task stack = createTaskStackOnDisplay(mDisplayContent);
- Task task = createTaskInStack(stack, 0 /* userId */);
+ // The display contains pinned root task that was added in {@link #setUp}.
+ Task rootTask = createTask(mDisplayContent);
+ Task task = createTaskInRootTask(rootTask, 0 /* userId */);
// Add another display at top.
mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
@@ -243,7 +242,7 @@
mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
// Move the task of {@code mDisplayContent} to top.
- stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+ rootTask.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
// Ensure that original display ({@code mDisplayContent}) is now on the top.
assertEquals("The testing DisplayContent should be moved to top with task",
@@ -252,16 +251,16 @@
@Test
public void testDontMovingChildTaskOnTop() {
- // Make sure the display is trusted display which capable to move the stack to top.
+ // Make sure the display is trusted display which capable to move the root task to top.
spyOn(mDisplayContent);
doReturn(true).when(mDisplayContent).isTrusted();
- // Allow child stack to move to top.
+ // Allow child root task to move to top.
mDisplayContent.mDontMoveToTop = true;
- // The display contains pinned stack that was added in {@link #setUp}.
- Task stack = createTaskStackOnDisplay(mDisplayContent);
- Task task = createTaskInStack(stack, 0 /* userId */);
+ // The display contains pinned root task that was added in {@link #setUp}.
+ Task rootTask = createTask(mDisplayContent);
+ Task task = createTaskInRootTask(rootTask, 0 /* userId */);
// Add another display at top.
mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
@@ -272,7 +271,7 @@
mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
// Try moving the task of {@code mDisplayContent} to top.
- stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+ rootTask.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
// Ensure that original display ({@code mDisplayContent}) hasn't moved and is not
// on the top.
@@ -282,8 +281,7 @@
@Test
public void testReuseTaskAsRootTask() {
- final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task candidateTask = createTask(mDisplayContent);
final int type = ACTIVITY_TYPE_STANDARD;
assertGetOrCreateRootTask(WINDOWING_MODE_FULLSCREEN, type, candidateTask,
true /* reuseCandidate */);
@@ -312,7 +310,7 @@
}
@Test
- public void testGetOrientation_nonResizableHomeStackWithHomeActivityPendingVisibilityChange() {
+ public void testGetOrientation_nonResizableHomeTaskWithHomeActivityPendingVisibilityChange() {
final RootWindowContainer rootWindowContainer = mWm.mAtmService.mRootWindowContainer;
final TaskDisplayArea defaultTaskDisplayArea =
rootWindowContainer.getDefaultTaskDisplayArea();
@@ -330,7 +328,7 @@
ActivityRecord primarySplitActivity = primarySplitTask.getTopNonFinishingActivity();
assertNotNull(primarySplitActivity);
primarySplitActivity.setState(RESUMED,
- "testGetOrientation_nonResizableHomeStackWithHomeActivityPendingVisibilityChange");
+ "testGetOrientation_nonResizableHomeTaskWithHomeActivityPendingVisibilityChange");
ActivityRecord homeActivity = rootHomeTask.getTopNonFinishingActivity();
if (homeActivity == null) {
@@ -350,14 +348,14 @@
final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
FEATURE_VENDOR_FIRST);
- final Task firstStack = firstTaskDisplayArea.createRootTask(
+ final Task firstRootTask = firstTaskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final Task secondStack = secondTaskDisplayArea.createRootTask(
+ final Task secondRootTask = secondTaskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
- .setTask(firstStack).build();
+ .setTask(firstRootTask).build();
final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
- .setTask(secondStack).build();
+ .setTask(secondRootTask).build();
// Activity on TDA1 is focused
mDisplayContent.setFocusedApp(firstActivity);
@@ -384,14 +382,14 @@
final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
FEATURE_VENDOR_FIRST);
- final Task firstStack = firstTaskDisplayArea.createRootTask(
+ final Task firstRootTask = firstTaskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final Task secondStack = secondTaskDisplayArea.createRootTask(
+ final Task secondRootTask = secondTaskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
- .setTask(firstStack).build();
+ .setTask(firstRootTask).build();
final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
- .setTask(secondStack).build();
+ .setTask(secondRootTask).build();
firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
@@ -411,9 +409,9 @@
@Test
public void testIgnoreOrientationRequest() {
final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
- final Task stack = taskDisplayArea.createRootTask(
+ final Task task = taskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
mDisplayContent.setFocusedApp(activity);
activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -428,7 +426,7 @@
@Test
@UseTestDisplay
public void testRemove_reparentToDefault() {
- final Task task = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTask(mDisplayContent);
final TaskDisplayArea displayArea = task.getDisplayArea();
displayArea.remove();
assertTrue(displayArea.isRemoved());
@@ -442,8 +440,8 @@
@Test
@UseTestDisplay
- public void testRemove_stackCreatedByOrganizer() {
- final Task task = createTaskStackOnDisplay(mDisplayContent);
+ public void testRemove_rootTaskCreatedByOrganizer() {
+ final Task task = createTask(mDisplayContent);
task.mCreatedByOrganizer = true;
final TaskDisplayArea displayArea = task.getDisplayArea();
displayArea.remove();
@@ -464,4 +462,221 @@
null /* activityOptions */);
assertEquals(reuseCandidate, rootTask == candidateTask);
}
+
+ @Test
+ public void testGetOrCreateRootHomeTask_defaultDisplay() {
+ TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea();
+
+ // Remove the current home root task if it exists so a new one can be created below.
+ Task homeTask = defaultTaskDisplayArea.getRootHomeTask();
+ if (homeTask != null) {
+ defaultTaskDisplayArea.removeChild(homeTask);
+ }
+ assertNull(defaultTaskDisplayArea.getRootHomeTask());
+
+ assertNotNull(defaultTaskDisplayArea.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() {
+ DisplayContent display = createNewDisplay();
+ doReturn(true).when(display).supportsSystemDecorations();
+
+ // Remove the current home root task if it exists so a new one can be created below.
+ TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+ Task homeTask = taskDisplayArea.getRootHomeTask();
+ if (homeTask != null) {
+ taskDisplayArea.removeChild(homeTask);
+ }
+ assertNull(taskDisplayArea.getRootHomeTask());
+
+ assertNotNull(taskDisplayArea.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() {
+ DisplayContent display = createNewDisplay();
+ TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+ doReturn(false).when(display).supportsSystemDecorations();
+
+ assertNull(taskDisplayArea.getRootHomeTask());
+ assertNull(taskDisplayArea.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testGetOrCreateRootHomeTask_untrustedDisplay() {
+ DisplayContent display = createNewDisplay();
+ TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+ doReturn(false).when(display).isTrusted();
+
+ assertNull(taskDisplayArea.getRootHomeTask());
+ assertNull(taskDisplayArea.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testGetOrCreateRootHomeTask_dontMoveToTop() {
+ DisplayContent display = createNewDisplay();
+ display.mDontMoveToTop = true;
+ TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+
+ assertNull(taskDisplayArea.getRootHomeTask());
+ assertNull(taskDisplayArea.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testLastFocusedRootTaskIsUpdatedWhenMovingRootTask() {
+ // Create a root task at bottom.
+ final TaskDisplayArea taskDisplayAreas =
+ mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea();
+ final Task rootTask =
+ new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
+ final Task prevFocusedRootTask = taskDisplayAreas.getFocusedRootTask();
+
+ rootTask.moveToFront("moveRootTaskToFront");
+ // After moving the root task to front, the previous focused should be the last focused.
+ assertTrue(rootTask.isFocusedRootTaskOnDisplay());
+ assertEquals(prevFocusedRootTask, taskDisplayAreas.getLastFocusedRootTask());
+
+ rootTask.moveToBack("moveRootTaskToBack", null /* task */);
+ // After moving the root task to back, the root task should be the last focused.
+ assertEquals(rootTask, taskDisplayAreas.getLastFocusedRootTask());
+ }
+
+ /**
+ * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
+ * root task. The fullscreen root task should be the top focused for resuming correctly.
+ */
+ @Test
+ public void testFullscreenRootTaskCanBeFocusedWhenFocusablePinnedRootTaskExists() {
+ // Create a pinned root task and move to front.
+ final Task pinnedRootTask = mRootWindowContainer.getDefaultTaskDisplayArea()
+ .createRootTask(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
+ final Task pinnedTask = new TaskBuilder(mAtm.mTaskSupervisor)
+ .setParentTask(pinnedRootTask).build();
+ new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
+ .setTask(pinnedTask).build();
+ pinnedRootTask.moveToFront("movePinnedRootTaskToFront");
+
+ // The focused root task should be the pinned root task.
+ assertTrue(pinnedRootTask.isFocusedRootTaskOnDisplay());
+
+ // Create a fullscreen root task and move to front.
+ final Task fullscreenRootTask = createTaskWithActivity(
+ mRootWindowContainer.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true);
+ fullscreenRootTask.moveToFront("moveFullscreenRootTaskToFront");
+
+ // The focused root task should be the fullscreen root task.
+ assertTrue(fullscreenRootTask.isFocusedRootTaskOnDisplay());
+ }
+
+ /**
+ * Test {@link TaskDisplayArea#mPreferredTopFocusableRootTask} will be cleared when
+ * the root task is removed or moved to back, and the focused root task will be according to
+ * z-order.
+ */
+ @Test
+ public void testRootTaskShouldNotBeFocusedAfterMovingToBackOrRemoving() {
+ // Create a display which only contains 2 root task.
+ final DisplayContent display = addNewDisplayContentAt(POSITION_TOP);
+ final Task rootTask1 = createTaskWithActivity(display.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */);
+ final Task rootTask2 = createTaskWithActivity(display.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */);
+
+ // Put rootTask1 and rootTask2 on top.
+ rootTask1.moveToFront("moveRootTask1ToFront");
+ rootTask2.moveToFront("moveRootTask2ToFront");
+ assertTrue(rootTask2.isFocusedRootTaskOnDisplay());
+
+ // rootTask1 should be focused after moving rootTask2 to back.
+ rootTask2.moveToBack("moveRootTask2ToBack", null /* task */);
+ assertTrue(rootTask1.isFocusedRootTaskOnDisplay());
+
+ // rootTask2 should be focused after removing rootTask1.
+ rootTask1.getDisplayArea().removeRootTask(rootTask1);
+ assertTrue(rootTask2.isFocusedRootTaskOnDisplay());
+ }
+
+ /**
+ * This test enforces that alwaysOnTop root task is placed at proper position.
+ */
+ @Test
+ public void testAlwaysOnTopRootTaskLocation() {
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ final Task alwaysOnTopRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(alwaysOnTopRootTask).build();
+ alwaysOnTopRootTask.setAlwaysOnTop(true);
+ taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopRootTask,
+ false /* includingParents */);
+ assertTrue(alwaysOnTopRootTask.isAlwaysOnTop());
+ // Ensure always on top state is synced to the children of the root task.
+ assertTrue(alwaysOnTopRootTask.getTopNonFinishingActivity().isAlwaysOnTop());
+ assertEquals(alwaysOnTopRootTask, taskDisplayArea.getTopRootTask());
+
+ final Task pinnedRootTask = taskDisplayArea.createRootTask(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ assertEquals(pinnedRootTask, taskDisplayArea.getRootPinnedTask());
+ assertEquals(pinnedRootTask, taskDisplayArea.getTopRootTask());
+
+ final Task anotherAlwaysOnTopRootTask = taskDisplayArea.createRootTask(
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ anotherAlwaysOnTopRootTask.setAlwaysOnTop(true);
+ taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopRootTask,
+ false /* includingParents */);
+ assertTrue(anotherAlwaysOnTopRootTask.isAlwaysOnTop());
+ int topPosition = taskDisplayArea.getRootTaskCount() - 1;
+ // Ensure the new alwaysOnTop root task is put below the pinned root task, but on top of the
+ // existing alwaysOnTop root task.
+ assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask));
+
+ final Task nonAlwaysOnTopRootTask = taskDisplayArea.createRootTask(
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ assertEquals(taskDisplayArea, nonAlwaysOnTopRootTask.getDisplayArea());
+ topPosition = taskDisplayArea.getRootTaskCount() - 1;
+ // Ensure the non-alwaysOnTop root task is put below the three alwaysOnTop root tasks, but
+ // above the existing other non-alwaysOnTop root tasks.
+ assertEquals(topPosition - 3, getTaskIndexOf(taskDisplayArea, nonAlwaysOnTopRootTask));
+
+ anotherAlwaysOnTopRootTask.setAlwaysOnTop(false);
+ taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopRootTask,
+ false /* includingParents */);
+ assertFalse(anotherAlwaysOnTopRootTask.isAlwaysOnTop());
+ // Ensure, when always on top is turned off for a root task, the root task is put just below
+ // all other always on top root tasks.
+ assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask));
+ anotherAlwaysOnTopRootTask.setAlwaysOnTop(true);
+
+ // Ensure always on top state changes properly when windowing mode changes.
+ anotherAlwaysOnTopRootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertFalse(anotherAlwaysOnTopRootTask.isAlwaysOnTop());
+ assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask));
+ anotherAlwaysOnTopRootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertTrue(anotherAlwaysOnTopRootTask.isAlwaysOnTop());
+ assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask));
+
+ final Task dreamRootTask = taskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */);
+ assertEquals(taskDisplayArea, dreamRootTask.getDisplayArea());
+ assertTrue(dreamRootTask.isAlwaysOnTop());
+ topPosition = taskDisplayArea.getRootTaskCount() - 1;
+ // Ensure dream shows above all activities, including PiP
+ assertEquals(dreamRootTask, taskDisplayArea.getTopRootTask());
+ assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, pinnedRootTask));
+
+ final Task assistRootTask = taskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
+ assertEquals(taskDisplayArea, assistRootTask.getDisplayArea());
+ assertFalse(assistRootTask.isAlwaysOnTop());
+ topPosition = taskDisplayArea.getRootTaskCount() - 1;
+
+ // Ensure Assistant shows as a non-always-on-top activity when config_assistantOnTopOfDream
+ // is false and on top of everything when true.
+ final boolean isAssistantOnTop = mContext.getResources()
+ .getBoolean(com.android.internal.R.bool.config_assistantOnTopOfDream);
+ assertEquals(isAssistantOnTop ? topPosition : topPosition - 4,
+ getTaskIndexOf(taskDisplayArea, assistRootTask));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index ed57294..de4c40d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -403,8 +403,8 @@
public void testOverridesDisplayAreaWithStandardTypeAndFullscreenMode() {
final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay,
mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
- final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+ final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD);
launchRoot.mCreatedByOrganizer = true;
secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FULLSCREEN },
@@ -419,8 +419,8 @@
public void testOverridesDisplayAreaWithHomeTypeAndFullscreenMode() {
final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay,
mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
- final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+ final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD);
launchRoot.mCreatedByOrganizer = true;
mActivity.setActivityType(ACTIVITY_TYPE_HOME);
@@ -438,8 +438,8 @@
WINDOWING_MODE_FREEFORM);
final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(freeformDisplay,
mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
- final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+ final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD);
launchRoot.mCreatedByOrganizer = true;
secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM },
@@ -455,8 +455,8 @@
public void testNotOverrideDisplayAreaWhenActivityOptionsHasDisplayArea() {
final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay,
mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
- final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+ final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD);
launchRoot.mCreatedByOrganizer = true;
secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FULLSCREEN },
@@ -481,8 +481,8 @@
mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
secondaryDisplayArea.setBounds(DISPLAY_BOUNDS.width() / 2, 0,
DISPLAY_BOUNDS.width(), DISPLAY_BOUNDS.height());
- final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+ final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD);
launchRoot.mCreatedByOrganizer = true;
secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM },
new int[] { ACTIVITY_TYPE_STANDARD });
@@ -512,8 +512,8 @@
mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
secondaryDisplayArea.setBounds(DISPLAY_BOUNDS.width() / 2, 0,
DISPLAY_BOUNDS.width(), DISPLAY_BOUNDS.height());
- final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, secondaryDisplayArea);
+ final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD);
launchRoot.mCreatedByOrganizer = true;
secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM },
new int[] { ACTIVITY_TYPE_STANDARD });
@@ -1687,16 +1687,16 @@
}
private ActivityRecord createSourceActivity(TestDisplayContent display) {
- final Task stack = display.getDefaultTaskDisplayArea()
+ final Task rootTask = display.getDefaultTaskDisplayArea()
.createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
- return new ActivityBuilder(mAtm).setTask(stack).build();
+ return new ActivityBuilder(mAtm).setTask(rootTask).build();
}
private void addFreeformTaskTo(TestDisplayContent display, Rect bounds) {
- final Task stack = display.getDefaultTaskDisplayArea()
+ final Task rootTask = display.getDefaultTaskDisplayArea()
.createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
- stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
- final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
+ rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
// Just work around the unnecessary adjustments for bounds.
task.getWindowConfiguration().setBounds(bounds);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
deleted file mode 100644
index d853b93..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ /dev/null
@@ -1,1161 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
-import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.util.DisplayMetrics.DENSITY_DEFAULT;
-import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_90;
-import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.sameInstance;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.same;
-
-import android.app.ActivityManager;
-import android.app.TaskInfo;
-import android.app.WindowConfiguration;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-import android.util.DisplayMetrics;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
-import android.util.Xml;
-import android.view.DisplayInfo;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-
-/**
- * Tests for exercising {@link Task}.
- *
- * Build/Install/Run:
- * atest WmTests:TaskRecordTests
- */
-@MediumTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class TaskRecordTests extends WindowTestsBase {
-
- private static final String TASK_TAG = "task";
-
- private Rect mParentBounds;
-
- @Before
- public void setUp() throws Exception {
- mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
- removeGlobalMinSizeRestriction();
- }
-
- @Test
- public void testRestoreWindowedTask() throws Exception {
- final Task expected = createTask(64);
- expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
-
- final byte[] serializedBytes = serializeToBytes(expected);
- final Task actual = restoreFromBytes(serializedBytes);
- assertEquals(expected.mTaskId, actual.mTaskId);
- assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
- }
-
- /** Ensure we have no chance to modify the original intent. */
- @Test
- public void testCopyBaseIntentForTaskInfo() {
- final Task task = createTask(1);
- task.setTaskDescription(new ActivityManager.TaskDescription());
- final TaskInfo info = task.getTaskInfo();
-
- // The intent of info should be a copy so assert that they are different instances.
- assertThat(info.baseIntent, not(sameInstance(task.getBaseIntent())));
- }
-
- @Test
- public void testReturnsToHomeStack() throws Exception {
- final Task task = createTask(1);
- spyOn(task);
- doReturn(true).when(task).hasChild();
- assertFalse(task.returnsToHomeRootTask());
- task.intent = null;
- assertFalse(task.returnsToHomeRootTask());
- task.intent = new Intent();
- assertFalse(task.returnsToHomeRootTask());
- task.intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
- assertTrue(task.returnsToHomeRootTask());
- }
-
- /** Ensures that empty bounds cause appBounds to inherit from parent. */
- @Test
- public void testAppBounds_EmptyBounds() {
- final Rect emptyBounds = new Rect();
- testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
- mParentBounds);
- }
-
- /** Ensures that bounds on freeform stacks are not clipped. */
- @Test
- public void testAppBounds_FreeFormBounds() {
- final Rect freeFormBounds = new Rect(mParentBounds);
- freeFormBounds.offset(10, 10);
- testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
- freeFormBounds);
- }
-
- /** Ensures that fully contained bounds are not clipped. */
- @Test
- public void testAppBounds_ContainedBounds() {
- final Rect insetBounds = new Rect(mParentBounds);
- insetBounds.inset(5, 5, 5, 5);
- testStackBoundsConfiguration(
- WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds);
- }
-
- @Test
- public void testFitWithinBounds() {
- final Rect parentBounds = new Rect(10, 10, 200, 200);
- TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
- Task stack = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
- ACTIVITY_TYPE_STANDARD, true /* onTop */);
- Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
- final Configuration parentConfig = stack.getConfiguration();
- parentConfig.windowConfiguration.setBounds(parentBounds);
- parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
-
- // check top and left
- Rect reqBounds = new Rect(-190, -190, 0, 0);
- task.setBounds(reqBounds);
- // Make sure part of it is exposed
- assertTrue(task.getBounds().right > parentBounds.left);
- assertTrue(task.getBounds().bottom > parentBounds.top);
- // Should still be more-or-less in that corner
- assertTrue(task.getBounds().left <= parentBounds.left);
- assertTrue(task.getBounds().top <= parentBounds.top);
-
- assertEquals(reqBounds.width(), task.getBounds().width());
- assertEquals(reqBounds.height(), task.getBounds().height());
-
- // check bottom and right
- reqBounds = new Rect(210, 210, 400, 400);
- task.setBounds(reqBounds);
- // Make sure part of it is exposed
- assertTrue(task.getBounds().left < parentBounds.right);
- assertTrue(task.getBounds().top < parentBounds.bottom);
- // Should still be more-or-less in that corner
- assertTrue(task.getBounds().right >= parentBounds.right);
- assertTrue(task.getBounds().bottom >= parentBounds.bottom);
-
- assertEquals(reqBounds.width(), task.getBounds().width());
- assertEquals(reqBounds.height(), task.getBounds().height());
- }
-
- /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
- @Test
- public void testBoundsOnModeChangeFreeformToFullscreen() {
- DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
- Task stack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true)
- .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
- Task task = stack.getBottomMostTask();
- task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
- DisplayInfo info = new DisplayInfo();
- display.mDisplay.getDisplayInfo(info);
- final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
- final Rect freeformBounds = new Rect(fullScreenBounds);
- freeformBounds.inset((int) (freeformBounds.width() * 0.2),
- (int) (freeformBounds.height() * 0.2));
- task.setBounds(freeformBounds);
-
- assertEquals(freeformBounds, task.getBounds());
-
- // FULLSCREEN inherits bounds
- stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertEquals(fullScreenBounds, task.getBounds());
- assertEquals(freeformBounds, task.mLastNonFullscreenBounds);
-
- // FREEFORM restores bounds
- stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(freeformBounds, task.getBounds());
- }
-
- /**
- * Tests that a task with forced orientation has orientation-consistent bounds within the
- * parent.
- */
- @Test
- public void testFullscreenBoundsForcedOrientation() {
- final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
- final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
- final DisplayContent display = new TestDisplayContent.Builder(mAtm,
- fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
- assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
- // Fix the display orientation to landscape which is the natural rotation (0) for the test
- // display.
- final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
- dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
- dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
-
- final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
- .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
- final Task task = stack.getBottomMostTask();
- final ActivityRecord root = task.getTopNonFinishingActivity();
-
- assertEquals(fullScreenBounds, task.getBounds());
-
- // Setting app to fixed portrait fits within parent
- root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
- assertEquals(root, task.getRootActivity());
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
- // Portrait orientation is enforced on activity level. Task should fill fullscreen bounds.
- assertThat(task.getBounds().height()).isLessThan(task.getBounds().width());
- assertEquals(fullScreenBounds, task.getBounds());
-
- // Top activity gets used
- final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(stack)
- .build();
- assertEquals(top, task.getTopNonFinishingActivity());
- top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
- assertEquals(task.getBounds().width(), fullScreenBounds.width());
-
- // Setting app to unspecified restores
- top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
- assertEquals(fullScreenBounds, task.getBounds());
-
- // Setting app to fixed landscape and changing display
- top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- // Fix the display orientation to portrait which is 90 degrees for the test display.
- dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
-
- // Fixed orientation request should be resolved on activity level. Task fills display
- // bounds.
- assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
- assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
- assertEquals(fullScreenBoundsPort, task.getBounds());
-
- // in FREEFORM, no constraint
- final Rect freeformBounds = new Rect(display.getBounds());
- freeformBounds.inset((int) (freeformBounds.width() * 0.2),
- (int) (freeformBounds.height() * 0.2));
- stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
- task.setBounds(freeformBounds);
- assertEquals(freeformBounds, task.getBounds());
-
- // FULLSCREEN letterboxes bounds on activity level, no constraint on task level.
- stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
- assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
- assertEquals(fullScreenBoundsPort, task.getBounds());
-
- // FREEFORM restores bounds as before
- stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(freeformBounds, task.getBounds());
- }
-
- @Test
- public void testReportsOrientationRequestInLetterboxForOrientation() {
- final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
- final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
- final DisplayContent display = new TestDisplayContent.Builder(mAtm,
- fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
- assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
- // Fix the display orientation to landscape which is the natural rotation (0) for the test
- // display.
- final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
- dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
- dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
-
- final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
- .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
- final Task task = stack.getBottomMostTask();
- ActivityRecord root = task.getTopNonFinishingActivity();
-
- assertEquals(fullScreenBounds, task.getBounds());
-
- // Setting app to fixed portrait fits within parent on activity level. Task fills parent.
- root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
- assertThat(root.getBounds().width()).isLessThan(root.getBounds().height());
- assertEquals(task.getBounds(), fullScreenBounds);
-
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation());
- }
-
- @Test
- public void testIgnoresForcedOrientationWhenParentHandles() {
- final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
- DisplayContent display = new TestDisplayContent.Builder(
- mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
-
- display.getRequestedOverrideConfiguration().orientation =
- Configuration.ORIENTATION_LANDSCAPE;
- display.onRequestedOverrideConfigurationChanged(
- display.getRequestedOverrideConfiguration());
- Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
- .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
- Task task = stack.getBottomMostTask();
- ActivityRecord root = task.getTopNonFinishingActivity();
-
- final WindowContainer parentWindowContainer =
- new WindowContainer(mSystemServicesTestRule.getWindowManagerService());
- spyOn(parentWindowContainer);
- parentWindowContainer.setBounds(fullScreenBounds);
- doReturn(parentWindowContainer).when(task).getParent();
- doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea();
- doReturn(stack).when(task).getRootTask();
- doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant();
-
- // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the
- // bounds because its parent says it will handle it at a later time.
- root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
- assertEquals(root, task.getRootActivity());
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
- assertEquals(fullScreenBounds, task.getBounds());
- }
-
- @Test
- public void testComputeConfigResourceOverrides() {
- final Rect fullScreenBounds = new Rect(0, 0, 1080, 1920);
- TestDisplayContent display = new TestDisplayContent.Builder(
- mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
- final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
- final Configuration inOutConfig = new Configuration();
- final Configuration parentConfig = new Configuration();
- final int longSide = 1200;
- final int shortSide = 600;
- final Rect parentBounds = new Rect(0, 0, 250, 500);
- final Rect parentAppBounds = new Rect(0, 0, 250, 480);
- parentConfig.windowConfiguration.setBounds(parentBounds);
- parentConfig.windowConfiguration.setAppBounds(parentAppBounds);
- parentConfig.densityDpi = 400;
- parentConfig.screenHeightDp = (parentBounds.bottom * 160) / parentConfig.densityDpi; // 200
- parentConfig.screenWidthDp = (parentBounds.right * 160) / parentConfig.densityDpi; // 100
- parentConfig.windowConfiguration.setRotation(ROTATION_0);
-
- // By default, the input bounds will fill parent.
- task.computeConfigResourceOverrides(inOutConfig, parentConfig);
-
- assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp);
- assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp);
- assertEquals(parentAppBounds, inOutConfig.windowConfiguration.getAppBounds());
- assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation);
-
- // If bounds are overridden, config properties should be made to match. Surface hierarchy
- // will crop for policy.
- inOutConfig.setToDefaults();
- final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide);
- inOutConfig.windowConfiguration.setBounds(largerPortraitBounds);
- task.computeConfigResourceOverrides(inOutConfig, parentConfig);
- // The override bounds are beyond the parent, the out appBounds should not be intersected
- // by parent appBounds.
- assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds());
- assertEquals(longSide, inOutConfig.screenHeightDp * parentConfig.densityDpi / 160);
- assertEquals(shortSide, inOutConfig.screenWidthDp * parentConfig.densityDpi / 160);
-
- inOutConfig.setToDefaults();
- // Landscape bounds.
- final Rect largerLandscapeBounds = new Rect(0, 0, longSide, shortSide);
- inOutConfig.windowConfiguration.setBounds(largerLandscapeBounds);
-
- // Setup the display with a top stable inset. The later assertion will ensure the inset is
- // excluded from screenHeightDp.
- final int statusBarHeight = 100;
- final DisplayPolicy policy = display.getDisplayPolicy();
- doAnswer(invocationOnMock -> {
- final Rect insets = invocationOnMock.<Rect>getArgument(0);
- insets.top = statusBarHeight;
- return null;
- }).when(policy).convertNonDecorInsetsToStableInsets(any(), eq(ROTATION_0));
-
- // Without limiting to be inside the parent bounds, the out screen size should keep relative
- // to the input bounds.
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
- final ActivityRecord.CompatDisplayInsets compatIntsets =
- new ActivityRecord.CompatDisplayInsets(
- display, activity, /* fixedOrientationBounds= */ null);
- task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
-
- assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
- assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi,
- inOutConfig.screenHeightDp);
- assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi,
- inOutConfig.screenWidthDp);
- assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation);
- }
-
- @Test
- public void testComputeConfigResourceLayoutOverrides() {
- final Rect fullScreenBounds = new Rect(0, 0, 1000, 2500);
- TestDisplayContent display = new TestDisplayContent.Builder(
- mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
- final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
- final Configuration inOutConfig = new Configuration();
- final Configuration parentConfig = new Configuration();
- final Rect nonLongBounds = new Rect(0, 0, 1000, 1250);
- parentConfig.windowConfiguration.setBounds(fullScreenBounds);
- parentConfig.windowConfiguration.setAppBounds(fullScreenBounds);
- parentConfig.densityDpi = 400;
- parentConfig.screenHeightDp = (fullScreenBounds.bottom * 160) / parentConfig.densityDpi;
- parentConfig.screenWidthDp = (fullScreenBounds.right * 160) / parentConfig.densityDpi;
- parentConfig.windowConfiguration.setRotation(ROTATION_0);
-
- // Set BOTH screenW/H to an override value
- inOutConfig.screenWidthDp = nonLongBounds.width() * 160 / parentConfig.densityDpi;
- inOutConfig.screenHeightDp = nonLongBounds.height() * 160 / parentConfig.densityDpi;
- task.computeConfigResourceOverrides(inOutConfig, parentConfig);
-
- // screenLayout should honor override when both screenW/H are set.
- assertTrue((inOutConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_NO) != 0);
- }
-
- @Test
- public void testComputeNestedConfigResourceOverrides() {
- final Task task = new TaskBuilder(mSupervisor).build();
- assertTrue(task.getResolvedOverrideBounds().isEmpty());
- int origScreenH = task.getConfiguration().screenHeightDp;
- Configuration stackConfig = new Configuration();
- stackConfig.setTo(task.getRootTask().getRequestedOverrideConfiguration());
- stackConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-
- // Set bounds on stack (not task) and verify that the task resource configuration changes
- // despite it's override bounds being empty.
- Rect bounds = new Rect(task.getRootTask().getBounds());
- bounds.bottom = (int) (bounds.bottom * 0.6f);
- stackConfig.windowConfiguration.setBounds(bounds);
- task.getRootTask().onRequestedOverrideConfigurationChanged(stackConfig);
- assertNotEquals(origScreenH, task.getConfiguration().screenHeightDp);
- }
-
- @Test
- public void testFullScreenTaskNotAdjustedByMinimalSize() {
- final Task fullscreenTask = new TaskBuilder(mSupervisor).build();
- final Rect originalTaskBounds = new Rect(fullscreenTask.getBounds());
- final ActivityInfo aInfo = new ActivityInfo();
- aInfo.windowLayout = new ActivityInfo.WindowLayout(0 /* width */, 0 /* widthFraction */,
- 0 /* height */, 0 /* heightFraction */, 0 /* gravity */,
- originalTaskBounds.width() * 2 /* minWidth */,
- originalTaskBounds.height() * 2 /* minHeight */);
- fullscreenTask.setMinDimensions(aInfo);
- fullscreenTask.onConfigurationChanged(fullscreenTask.getParent().getConfiguration());
-
- assertEquals(originalTaskBounds, fullscreenTask.getBounds());
- }
-
- @Test
- public void testInsetDisregardedWhenFreeformOverlapsNavBar() {
- TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
- Task stack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, true /* onTop */);
- DisplayInfo displayInfo = new DisplayInfo();
- mAtm.mContext.getDisplay().getDisplayInfo(displayInfo);
- final int displayHeight = displayInfo.logicalHeight;
- final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
- final Configuration inOutConfig = new Configuration();
- final Configuration parentConfig = new Configuration();
- final int longSide = 1200;
- final int shortSide = 600;
- parentConfig.densityDpi = 400;
- parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px
- parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px
- parentConfig.windowConfiguration.setRotation(ROTATION_0);
-
- final int longSideDp = 480; // longSide / density = 1200 / 400 * 160
- final int shortSideDp = 240; // shortSide / density = 600 / 400 * 160
- final int screenLayout = parentConfig.screenLayout
- & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
- final int reducedScreenLayout =
- Configuration.reduceScreenLayout(screenLayout, longSideDp, shortSideDp);
-
- // Portrait bounds overlapping with navigation bar, without insets.
- final Rect freeformBounds = new Rect(0,
- displayHeight - 10 - longSide,
- shortSide,
- displayHeight - 10);
- inOutConfig.windowConfiguration.setBounds(freeformBounds);
- // Set to freeform mode to verify bug fix.
- inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-
- task.computeConfigResourceOverrides(inOutConfig, parentConfig);
-
- // screenW/H should not be effected by parent since overridden and freeform
- assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
- inOutConfig.screenWidthDp);
- assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
- inOutConfig.screenHeightDp);
- assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
-
- inOutConfig.setToDefaults();
- // Landscape bounds overlapping with navigtion bar, without insets.
- freeformBounds.set(0,
- displayHeight - 10 - shortSide,
- longSide,
- displayHeight - 10);
- inOutConfig.windowConfiguration.setBounds(freeformBounds);
- inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-
- task.computeConfigResourceOverrides(inOutConfig, parentConfig);
-
- assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
- inOutConfig.screenWidthDp);
- assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
- inOutConfig.screenHeightDp);
- assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
- }
-
- /** Ensures that the alias intent won't have target component resolved. */
- @Test
- public void testTaskIntentActivityAlias() {
- final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
- final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
- final ComponentName aliasComponent =
- new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName);
- final ComponentName targetComponent =
- new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
-
- final Intent intent = new Intent();
- intent.setComponent(aliasComponent);
- final ActivityInfo info = new ActivityInfo();
- info.applicationInfo = new ApplicationInfo();
- info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
- info.targetActivity = targetClassName;
-
- final Task task = new Task.Builder(mAtm)
- .setTaskId(1)
- .setActivityInfo(info)
- .setIntent(intent)
- .build();
- assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
- task.intent.getComponent().getClassName());
-
- ActivityRecord aliasActivity = new ActivityBuilder(mAtm).setComponent(
- aliasComponent).setTargetActivity(targetClassName).build();
- assertEquals("Should be the same intent filter.", true,
- task.isSameIntentFilter(aliasActivity));
-
- ActivityRecord targetActivity = new ActivityBuilder(mAtm).setComponent(
- targetComponent).build();
- assertEquals("Should be the same intent filter.", true,
- task.isSameIntentFilter(targetActivity));
-
- ActivityRecord defaultActivity = new ActivityBuilder(mAtm).build();
- assertEquals("Should not be the same intent filter.", false,
- task.isSameIntentFilter(defaultActivity));
- }
-
- /** Test that root activity index is reported correctly for several activities in the task. */
- @Test
- public void testFindRootIndex() {
- final Task task = getTestTask();
- // Add an extra activity on top of the root one
- new ActivityBuilder(mAtm).setTask(task).build();
-
- assertEquals("The root activity in the task must be reported.", task.getChildAt(0),
- task.getRootActivity(
- true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
- }
-
- /**
- * Test that root activity index is reported correctly for several activities in the task when
- * the activities on the bottom are finishing.
- */
- @Test
- public void testFindRootIndex_finishing() {
- final Task task = getTestTask();
- // Add extra two activities and mark the two on the bottom as finishing.
- final ActivityRecord activity0 = task.getBottomMostActivity();
- activity0.finishing = true;
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
- activity1.finishing = true;
- new ActivityBuilder(mAtm).setTask(task).build();
-
- assertEquals("The first non-finishing activity in the task must be reported.",
- task.getChildAt(2), task.getRootActivity(
- true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
- }
-
- /**
- * Test that root activity index is reported correctly for several activities in the task when
- * looking for the 'effective root'.
- */
- @Test
- public void testFindRootIndex_effectiveRoot() {
- final Task task = getTestTask();
- // Add an extra activity on top of the root one
- new ActivityBuilder(mAtm).setTask(task).build();
-
- assertEquals("The root activity in the task must be reported.",
- task.getChildAt(0), task.getRootActivity(
- false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
- }
-
- /**
- * Test that root activity index is reported correctly when looking for the 'effective root' in
- * case when bottom activities are relinquishing task identity or finishing.
- */
- @Test
- public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() {
- final Task task = getTestTask();
- // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
- // one above as finishing.
- final ActivityRecord activity0 = task.getBottomMostActivity();
- activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
- activity1.finishing = true;
- new ActivityBuilder(mAtm).setTask(task).build();
-
- assertEquals("The first non-finishing activity and non-relinquishing task identity "
- + "must be reported.", task.getChildAt(2), task.getRootActivity(
- false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
- }
-
- /**
- * Test that root activity index is reported correctly when looking for the 'effective root'
- * for the case when there is only a single activity that also has relinquishTaskIdentity set.
- */
- @Test
- public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() {
- final Task task = getTestTask();
- // Set relinquishTaskIdentity for the only activity in the task
- task.getBottomMostActivity().info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
-
- assertEquals("The root activity in the task must be reported.",
- task.getChildAt(0), task.getRootActivity(
- false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
- }
-
- /**
- * Test that the topmost activity index is reported correctly when looking for the
- * 'effective root' for the case when all activities have relinquishTaskIdentity set.
- */
- @Test
- public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
- final Task task = getTestTask();
- // Set relinquishTaskIdentity for all activities in the task
- final ActivityRecord activity0 = task.getBottomMostActivity();
- activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
- activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
-
- assertEquals("The topmost activity in the task must be reported.",
- task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
- false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
- }
-
- /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
- @Test
- public void testGetRootActivity() {
- final Task task = getTestTask();
- // Add an extra activity on top of the root one
- new ActivityBuilder(mAtm).setTask(task).build();
-
- assertEquals("The root activity in the task must be reported.",
- task.getBottomMostActivity(), task.getRootActivity());
- }
-
- /**
- * Test that first non-finishing activity is reported in {@link Task#getRootActivity()}.
- */
- @Test
- public void testGetRootActivity_finishing() {
- final Task task = getTestTask();
- // Add an extra activity on top of the root one
- new ActivityBuilder(mAtm).setTask(task).build();
- // Mark the root as finishing
- task.getBottomMostActivity().finishing = true;
-
- assertEquals("The first non-finishing activity in the task must be reported.",
- task.getChildAt(1), task.getRootActivity());
- }
-
- /**
- * Test that relinquishTaskIdentity flag is ignored in {@link Task#getRootActivity()}.
- */
- @Test
- public void testGetRootActivity_relinquishTaskIdentity() {
- final Task task = getTestTask();
- // Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY.
- final ActivityRecord activity0 = task.getBottomMostActivity();
- activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
- // Add an extra activity on top of the root one.
- new ActivityBuilder(mAtm).setTask(task).build();
-
- assertEquals("The root activity in the task must be reported.",
- task.getBottomMostActivity(), task.getRootActivity());
- }
-
- /**
- * Test that no activity is reported in {@link Task#getRootActivity()} when all activities
- * in the task are finishing.
- */
- @Test
- public void testGetRootActivity_allFinishing() {
- final Task task = getTestTask();
- // Mark the bottom-most activity as finishing.
- final ActivityRecord activity0 = task.getBottomMostActivity();
- activity0.finishing = true;
- // Add an extra activity on top of the root one and mark it as finishing
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
- activity1.finishing = true;
-
- assertNull("No activity must be reported if all are finishing", task.getRootActivity());
- }
-
- /**
- * Test that first non-finishing activity is the root of task.
- */
- @Test
- public void testIsRootActivity() {
- final Task task = getTestTask();
- // Mark the bottom-most activity as finishing.
- final ActivityRecord activity0 = task.getBottomMostActivity();
- activity0.finishing = true;
- // Add an extra activity on top of the root one.
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-
- assertFalse("Finishing activity must not be the root of task", activity0.isRootOfTask());
- assertTrue("Non-finishing activity must be the root of task", activity1.isRootOfTask());
- }
-
- /**
- * Test that if all activities in the task are finishing, then the one on the bottom is the
- * root of task.
- */
- @Test
- public void testIsRootActivity_allFinishing() {
- final Task task = getTestTask();
- // Mark the bottom-most activity as finishing.
- final ActivityRecord activity0 = task.getBottomMostActivity();
- activity0.finishing = true;
- // Add an extra activity on top of the root one and mark it as finishing
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
- activity1.finishing = true;
-
- assertTrue("Bottom activity must be the root of task", activity0.isRootOfTask());
- assertFalse("Finishing activity on top must not be the root of task",
- activity1.isRootOfTask());
- }
-
- /**
- * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)}.
- */
- @Test
- public void testGetTaskForActivity() {
- final Task task0 = getTestTask();
- final ActivityRecord activity0 = task0.getBottomMostActivity();
-
- final Task task1 = getTestTask();
- final ActivityRecord activity1 = task1.getBottomMostActivity();
-
- assertEquals(task0.mTaskId,
- ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
- assertEquals(task1.mTaskId,
- ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */));
- }
-
- /**
- * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with finishing
- * activity.
- */
- @Test
- public void testGetTaskForActivity_onlyRoot_finishing() {
- final Task task = getTestTask();
- // Make the current root activity finishing
- final ActivityRecord activity0 = task.getBottomMostActivity();
- activity0.finishing = true;
- // Add an extra activity on top - this will be the new root
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
- // Add one more on top
- final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
-
- assertEquals(task.mTaskId,
- ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
- assertEquals(task.mTaskId,
- ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
- assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
- ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
- }
-
- /**
- * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that
- * relinquishes task identity.
- */
- @Test
- public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
- final Task task = getTestTask();
- // Make the current root activity relinquish task identity
- final ActivityRecord activity0 = task.getBottomMostActivity();
- activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
- // Add an extra activity on top - this will be the new root
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
- // Add one more on top
- final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
-
- assertEquals(task.mTaskId,
- ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
- assertEquals(task.mTaskId,
- ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
- assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
- ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
- }
-
- /**
- * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} allowing non-root
- * entries.
- */
- @Test
- public void testGetTaskForActivity_notOnlyRoot() {
- final Task task = getTestTask();
- // Mark the bottom-most activity as finishing.
- final ActivityRecord activity0 = task.getBottomMostActivity();
- activity0.finishing = true;
-
- // Add an extra activity on top of the root one and make it relinquish task identity
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
- activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
-
- // Add one more activity on top
- final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
-
- assertEquals(task.mTaskId,
- ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
- assertEquals(task.mTaskId,
- ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */));
- assertEquals(task.mTaskId,
- ActivityRecord.getTaskForActivityLocked(activity2.appToken, false /* onlyRoot */));
- }
-
- /**
- * Test {@link Task#updateEffectiveIntent()}.
- */
- @Test
- public void testUpdateEffectiveIntent() {
- // Test simple case with a single activity.
- final Task task = getTestTask();
- final ActivityRecord activity0 = task.getBottomMostActivity();
-
- spyOn(task);
- task.updateEffectiveIntent();
- verify(task).setIntent(eq(activity0));
- }
-
- /**
- * Test {@link Task#updateEffectiveIntent()} with root activity marked as finishing. This
- * should make the task use the second activity when updating the intent.
- */
- @Test
- public void testUpdateEffectiveIntent_rootFinishing() {
- // Test simple case with a single activity.
- final Task task = getTestTask();
- final ActivityRecord activity0 = task.getBottomMostActivity();
- // Mark the bottom-most activity as finishing.
- activity0.finishing = true;
- // Add an extra activity on top of the root one
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
-
- spyOn(task);
- task.updateEffectiveIntent();
- verify(task).setIntent(eq(activity1));
- }
-
- /**
- * Test {@link Task#updateEffectiveIntent()} when all activities are finishing or
- * relinquishing task identity. In this case the root activity should still be used when
- * updating the intent (legacy behavior).
- */
- @Test
- public void testUpdateEffectiveIntent_allFinishing() {
- // Test simple case with a single activity.
- final Task task = getTestTask();
- final ActivityRecord activity0 = task.getBottomMostActivity();
- // Mark the bottom-most activity as finishing.
- activity0.finishing = true;
- // Add an extra activity on top of the root one and make it relinquish task identity
- final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
- activity1.finishing = true;
-
- // Task must still update the intent using the root activity (preserving legacy behavior).
- spyOn(task);
- task.updateEffectiveIntent();
- verify(task).setIntent(eq(activity0));
- }
-
- @Test
- public void testSaveLaunchingStateWhenConfigurationChanged() {
- LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
- spyOn(persister);
-
- final Task task = getTestTask();
- task.setHasBeenVisible(false);
- task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
- task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- task.setHasBeenVisible(true);
- task.onConfigurationChanged(task.getParent().getConfiguration());
-
- verify(persister).saveTask(task, task.getDisplayContent());
- }
-
- @Test
- public void testSaveLaunchingStateWhenClearingParent() {
- LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
- spyOn(persister);
-
- final Task task = getTestTask();
- task.setHasBeenVisible(false);
- task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
- task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final DisplayContent oldDisplay = task.getDisplayContent();
-
- LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams();
- params.mWindowingMode = WINDOWING_MODE_UNDEFINED;
- persister.getLaunchParams(task, null, params);
- assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode);
-
- task.setHasBeenVisible(true);
- task.removeImmediately();
-
- verify(persister).saveTask(task, oldDisplay);
-
- persister.getLaunchParams(task, null, params);
- assertEquals(WINDOWING_MODE_FULLSCREEN, params.mWindowingMode);
- }
-
- @Test
- public void testNotSaveLaunchingStateNonFreeformDisplay() {
- LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
- spyOn(persister);
-
- final Task task = getTestTask();
- task.setHasBeenVisible(false);
- task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- task.setHasBeenVisible(true);
- task.onConfigurationChanged(task.getParent().getConfiguration());
-
- verify(persister, never()).saveTask(same(task), any());
- }
-
- @Test
- public void testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow() {
- LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
- spyOn(persister);
-
- final Task task = getTestTask();
- task.setHasBeenVisible(false);
- task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
- task.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
-
- task.setHasBeenVisible(true);
- task.onConfigurationChanged(task.getParent().getConfiguration());
-
- verify(persister, never()).saveTask(same(task), any());
- }
-
- @Test
- public void testNotSaveLaunchingStateForNonLeafTask() {
- LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
- spyOn(persister);
-
- final Task task = getTestTask();
- task.setHasBeenVisible(false);
- task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
- task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- final Task leafTask = createTaskInStack(task, 0 /* userId */);
-
- leafTask.setHasBeenVisible(true);
- task.setHasBeenVisible(true);
- task.onConfigurationChanged(task.getParent().getConfiguration());
-
- verify(persister, never()).saveTask(same(task), any());
- verify(persister).saveTask(same(leafTask), any());
- }
-
- @Test
- public void testNotSpecifyOrientationByFloatingTask() {
- final Task task = new TaskBuilder(mSupervisor)
- .setCreateActivity(true).setCreateParentTask(true).build();
- 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);
-
- // TDA returns the last orientation when child returns UNSET
- assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation());
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
- }
-
- @Test
- public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() {
- final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
- final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
- mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
- FEATURE_VENDOR_FIRST);
- final Task firstStack = firstTaskDisplayArea.createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final Task secondStack = secondTaskDisplayArea.createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
- .setTask(firstStack).build();
- final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
- .setTask(secondStack).build();
- firstActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- secondActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
-
- // Activity on TDA1 is focused
- mDisplayContent.setFocusedApp(firstActivity);
-
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
- assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
-
- // No focused app, TDA1 is still recorded as last focused.
- mDisplayContent.setFocusedApp(null);
-
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
- assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
-
- // Activity on TDA2 is focused
- mDisplayContent.setFocusedApp(secondActivity);
-
- assertEquals(SCREEN_ORIENTATION_UNSET, firstTaskDisplayArea.getOrientation());
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, secondTaskDisplayArea.getOrientation());
- }
-
- @Test
- public void testNotifyOrientationChangeCausedByConfigurationChange() {
- final Task task = getTestTask();
- final ActivityRecord activity = task.getTopMostActivity();
- final DisplayContent display = task.getDisplayContent();
- display.setWindowingMode(WINDOWING_MODE_FREEFORM);
-
- activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
- verify(display).onDescendantOrientationChanged(same(task));
- reset(display);
-
- display.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation());
- verify(display).onDescendantOrientationChanged(same(task));
- }
-
- private Task getTestTask() {
- final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
- return stack.getBottomMostTask();
- }
-
- private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
- Rect expectedConfigBounds) {
-
- TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
- Task stack = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
-
- final Configuration parentConfig = stack.getConfiguration();
- parentConfig.windowConfiguration.setAppBounds(parentBounds);
- task.setBounds(bounds);
-
- task.resolveOverrideConfiguration(parentConfig);
- // Assert that both expected and actual are null or are equal to each other
- assertEquals(expectedConfigBounds,
- task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds());
- }
-
- private byte[] serializeToBytes(Task r) throws Exception {
- try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
- final TypedXmlSerializer serializer = Xml.newFastSerializer();
- serializer.setOutput(os, "UTF-8");
- serializer.startDocument(null, true);
- serializer.startTag(null, TASK_TAG);
- r.saveToXml(serializer);
- serializer.endTag(null, TASK_TAG);
- serializer.endDocument();
-
- os.flush();
- return os.toByteArray();
- }
- }
-
- private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
- try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
- final TypedXmlPullParser parser = Xml.newFastPullParser();
- parser.setInput(reader);
- assertEquals(XmlPullParser.START_TAG, parser.next());
- assertEquals(TASK_TAG, parser.getName());
- return Task.restoreFromXml(parser, mAtm.mTaskSupervisor);
- }
- }
-
- private Task createTask(int taskId) {
- return new Task.Builder(mAtm)
- .setTaskId(taskId)
- .setIntent(new Intent())
- .setRealActivity(ActivityBuilder.getDefaultComponent())
- .setEffectiveUid(10050)
- .buildInner();
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
deleted file mode 100644
index e58c162..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-
-import android.app.WindowConfiguration;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link Task} class.
- *
- * Build/Install/Run:
- * atest WmTests:TaskStackTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class TaskStackTests extends WindowTestsBase {
-
- @Test
- public void testStackPositionChildAt() {
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task1 = createTaskInStack(stack, 0 /* userId */);
- final Task task2 = createTaskInStack(stack, 1 /* userId */);
-
- // Current user task should be moved to top.
- stack.positionChildAt(WindowContainer.POSITION_TOP, task1, false /* includingParents */);
- assertEquals(stack.mChildren.get(0), task2);
- assertEquals(stack.mChildren.get(1), task1);
-
- // Non-current user won't be moved to top.
- stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
- assertEquals(stack.mChildren.get(0), task2);
- assertEquals(stack.mChildren.get(1), task1);
-
- // Non-leaf task should be moved to top regardless of the user id.
- createTaskInStack(task2, 0 /* userId */);
- createTaskInStack(task2, 1 /* userId */);
- stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
- assertEquals(stack.mChildren.get(0), task1);
- assertEquals(stack.mChildren.get(1), task2);
- }
-
- @Test
- public void testClosingAppDifferentTaskOrientation() {
- final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
- activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-
- final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
- activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
-
- final WindowContainer parent = activity1.getTask().getParent();
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation());
- mDisplayContent.mClosingApps.add(activity2);
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parent.getOrientation());
- }
-
- @Test
- public void testMoveTaskToBackDifferentTaskOrientation() {
- final ActivityRecord activity1 = createActivityRecord(mDisplayContent);
- activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-
- final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
- activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
-
- final WindowContainer parent = activity1.getTask().getParent();
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation());
- }
-
- @Test
- public void testStackRemoveImmediately() {
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- assertEquals(stack, task.getRootTask());
-
- // Remove stack and check if its child is also removed.
- stack.removeImmediately();
- assertNull(stack.getDisplayContent());
- assertNull(task.getParent());
- }
-
- @Test
- public void testRemoveContainer() {
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
-
- assertNotNull(stack);
- assertNotNull(task);
- stack.removeIfPossible();
- // Assert that the container was removed.
- assertNull(stack.getParent());
- assertEquals(0, stack.getChildCount());
- assertNull(stack.getDisplayContent());
- assertNull(task.getDisplayContent());
- assertNull(task.getParent());
- }
-
- @Test
- public void testRemoveContainer_deferRemoval() {
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
-
- // Stack removal is deferred if one of its child is animating.
- doReturn(true).when(stack).hasWindowsAlive();
- doReturn(stack).when(task).getAnimatingContainer(
- eq(TRANSITION | CHILDREN), anyInt());
-
- stack.removeIfPossible();
- // For the case of deferred removal the task controller will still be connected to the its
- // container until the stack window container is removed.
- assertNotNull(stack.getParent());
- assertNotEquals(0, stack.getChildCount());
- assertNotNull(task);
-
- stack.removeImmediately();
- // After removing, the task will be isolated.
- assertNull(task.getParent());
- assertEquals(0, task.getChildCount());
- }
-
- @Test
- public void testReparent() {
- // Create first stack on primary display.
- final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
- final Task task1 = createTaskInStack(stack1, 0 /* userId */);
-
- // Create second display and put second stack on it.
- final DisplayContent dc = createNewDisplay();
- final Task stack2 = createTaskStackOnDisplay(dc);
-
- // Reparent
- clearInvocations(task1); // reset the number of onDisplayChanged for task.
- stack1.reparent(dc.getDefaultTaskDisplayArea(), true /* onTop */);
- assertEquals(dc, stack1.getDisplayContent());
- final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1);
- final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2);
- assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
- verify(task1, times(1)).onDisplayChanged(any());
- }
-
- @Test
- public void testStackOutset() {
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
- final int stackOutset = 10;
- spyOn(stack);
- doReturn(stackOutset).when(stack).getTaskOutset();
- doReturn(true).when(stack).inMultiWindowMode();
-
- // Mock the resolved override windowing mode to non-fullscreen
- final WindowConfiguration windowConfiguration =
- stack.getResolvedOverrideConfiguration().windowConfiguration;
- spyOn(windowConfiguration);
- doReturn(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
- .when(windowConfiguration).getWindowingMode();
-
- // Prevent adjust task dimensions
- doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any(), any());
-
- final Rect stackBounds = new Rect(200, 200, 800, 1000);
- // Update surface position and size by the given bounds.
- stack.setBounds(stackBounds);
-
- assertEquals(stackBounds.width() + 2 * stackOutset, stack.getLastSurfaceSize().x);
- assertEquals(stackBounds.height() + 2 * stackOutset, stack.getLastSurfaceSize().y);
- assertEquals(stackBounds.left - stackOutset, stack.getLastSurfacePosition().x);
- assertEquals(stackBounds.top - stackOutset, stack.getLastSurfacePosition().y);
- }
-
- @Test
- public void testActivityAndTaskGetsProperType() {
- final Task task1 = new TaskBuilder(mSupervisor).build();
- ActivityRecord activity1 = createNonAttachedActivityRecord(mDisplayContent);
-
- // First activity should become standard
- task1.addChild(activity1, 0);
- assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity1.getActivityType());
- assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType());
-
- // Second activity should also become standard
- ActivityRecord activity2 = createNonAttachedActivityRecord(mDisplayContent);
- task1.addChild(activity2, WindowContainer.POSITION_TOP);
- assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity2.getActivityType());
- assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType());
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index dca6b08..2389d2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -16,20 +16,39 @@
package com.android.server.wm;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.google.common.truth.Truth.assertThat;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -39,18 +58,45 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.util.DisplayMetrics;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+import android.view.DisplayInfo;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
+import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
/**
* Test class for {@link Task}.
@@ -58,15 +104,25 @@
* Build/Install/Run:
* atest WmTests:TaskTests
*/
-@SmallTest
+@MediumTest
@Presubmit
@RunWith(WindowTestRunner.class)
public class TaskTests extends WindowTestsBase {
+ private static final String TASK_TAG = "task";
+
+ private Rect mParentBounds;
+
+ @Before
+ public void setUp() throws Exception {
+ mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
+ removeGlobalMinSizeRestriction();
+ }
+
@Test
public void testRemoveContainer() {
- final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stackController1, 0 /* userId */);
+ final Task taskController1 = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
task.removeIfPossible();
@@ -78,8 +134,8 @@
@Test
public void testRemoveContainer_deferRemoval() {
- final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stackController1, 0 /* userId */);
+ final Task taskController1 = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
doReturn(true).when(task).shouldDeferRemoval();
@@ -99,14 +155,14 @@
@Test
public void testReparent() {
- final Task stackController1 = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stackController1, 0 /* userId */);
- final Task stackController2 = createTaskStackOnDisplay(mDisplayContent);
- final Task task2 = createTaskInStack(stackController2, 0 /* userId */);
+ final Task taskController1 = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
+ final Task taskController2 = createTask(mDisplayContent);
+ final Task task2 = createTaskInRootTask(taskController2, 0 /* userId */);
boolean gotException = false;
try {
- task.reparent(stackController1, 0, false/* moveParents */, "testReparent");
+ task.reparent(taskController1, 0, false/* moveParents */, "testReparent");
} catch (IllegalArgumentException e) {
gotException = true;
}
@@ -118,29 +174,29 @@
} catch (Exception e) {
gotException = true;
}
- assertTrue("Should not be able to reparent to a stack that doesn't exist", gotException);
+ assertTrue("Should not be able to reparent to a task that doesn't exist", gotException);
- task.reparent(stackController2, 0, false/* moveParents */, "testReparent");
- assertEquals(stackController2, task.getParent());
+ task.reparent(taskController2, 0, false/* moveParents */, "testReparent");
+ assertEquals(taskController2, task.getParent());
assertEquals(0, task.getParent().mChildren.indexOf(task));
assertEquals(1, task2.getParent().mChildren.indexOf(task2));
}
@Test
public void testReparent_BetweenDisplays() {
- // Create first stack on primary display.
- final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack1, 0 /* userId */);
- assertEquals(mDisplayContent, stack1.getDisplayContent());
+ // Create first task on primary display.
+ final Task rootTask1 = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask1, 0 /* userId */);
+ assertEquals(mDisplayContent, rootTask1.getDisplayContent());
- // Create second display and put second stack on it.
+ // Create second display and put second task on it.
final DisplayContent dc = createNewDisplay();
- final Task stack2 = createTaskStackOnDisplay(dc);
- final Task task2 = createTaskInStack(stack2, 0 /* userId */);
+ final Task rootTask2 = createTask(dc);
+ final Task task2 = createTaskInRootTask(rootTask2, 0 /* userId */);
// Reparent and check state
clearInvocations(task); // reset the number of onDisplayChanged for task.
- task.reparent(stack2, 0, false /* moveParents */, "testReparent_BetweenDisplays");
- assertEquals(stack2, task.getParent());
+ task.reparent(rootTask2, 0, false /* moveParents */, "testReparent_BetweenDisplays");
+ assertEquals(rootTask2, task.getParent());
assertEquals(0, task.getParent().mChildren.indexOf(task));
assertEquals(1, task2.getParent().mChildren.indexOf(task2));
verify(task, times(1)).onDisplayChanged(any());
@@ -148,8 +204,8 @@
@Test
public void testBounds() {
- final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack1, 0 /* userId */);
+ final Task rootTask1 = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask1, 0 /* userId */);
// Check that setting bounds also updates surface position
task.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -159,9 +215,9 @@
}
@Test
- public void testIsInStack() {
- final Task task1 = createTaskStackOnDisplay(mDisplayContent);
- final Task task2 = createTaskStackOnDisplay(mDisplayContent);
+ public void testIsInTask() {
+ final Task task1 = createTask(mDisplayContent);
+ final Task task2 = createTask(mDisplayContent);
final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task1);
final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task2);
assertEquals(activity1, task1.isInTask(activity1));
@@ -170,7 +226,7 @@
@Test
public void testRemoveChildForOverlayTask() {
- final Task task = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTask(mDisplayContent);
final int taskId = task.mTaskId;
final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task);
final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task);
@@ -193,10 +249,10 @@
@Test
public void testSwitchUser() {
- final Task rootTask = createTaskStackOnDisplay(mDisplayContent);
- final Task childTask = createTaskInStack(rootTask, 0 /* userId */);
- final Task leafTask1 = createTaskInStack(childTask, 10 /* userId */);
- final Task leafTask2 = createTaskInStack(childTask, 0 /* userId */);
+ final Task rootTask = createTask(mDisplayContent);
+ final Task childTask = createTaskInRootTask(rootTask, 0 /* userId */);
+ final Task leafTask1 = createTaskInRootTask(childTask, 10 /* userId */);
+ final Task leafTask2 = createTaskInRootTask(childTask, 0 /* userId */);
assertEquals(1, rootTask.getChildCount());
assertEquals(leafTask2, childTask.getTopChild());
@@ -208,9 +264,9 @@
@Test
public void testEnsureActivitiesVisible() {
- final Task rootTask = createTaskStackOnDisplay(mDisplayContent);
- final Task leafTask1 = createTaskInStack(rootTask, 0 /* userId */);
- final Task leafTask2 = createTaskInStack(rootTask, 0 /* userId */);
+ final Task rootTask = createTask(mDisplayContent);
+ final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */);
+ final Task leafTask2 = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity1 = createActivityRecord(mDisplayContent, leafTask1);
final ActivityRecord activity2 = createActivityRecord(mDisplayContent, leafTask2);
@@ -233,7 +289,7 @@
@Test
public void testResolveNonResizableTaskWindowingMode() {
- final Task task = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTask(mDisplayContent);
Configuration parentConfig = task.getParent().getConfiguration();
parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
doReturn(false).when(task).isResizeable();
@@ -264,10 +320,10 @@
@Test
public void testHandlesOrientationChangeFromDescendant() {
- final Task rootTask = createTaskStackOnDisplay(WINDOWING_MODE_MULTI_WINDOW,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
- final Task leafTask1 = createTaskInStack(rootTask, 0 /* userId */);
- final Task leafTask2 = createTaskInStack(rootTask, 0 /* userId */);
+ final Task rootTask = createTask(mDisplayContent,
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */);
+ final Task leafTask2 = createTaskInRootTask(rootTask, 0 /* userId */);
leafTask1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_HOME);
leafTask2.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_STANDARD);
@@ -281,7 +337,7 @@
@Test
public void testAlwaysOnTop() {
- final Task task = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTask(mDisplayContent);
task.setAlwaysOnTop(true);
task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
assertTrue(task.isAlwaysOnTop());
@@ -289,4 +345,1053 @@
task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
assertFalse(task.isAlwaysOnTop());
}
+
+ @Test
+ public void testRestoreWindowedTask() throws Exception {
+ final Task expected = createTask(64);
+ expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
+
+ final byte[] serializedBytes = serializeToBytes(expected);
+ final Task actual = restoreFromBytes(serializedBytes);
+ assertEquals(expected.mTaskId, actual.mTaskId);
+ assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
+ }
+
+ /** Ensure we have no chance to modify the original intent. */
+ @Test
+ public void testCopyBaseIntentForTaskInfo() {
+ final Task task = createTask(1);
+ task.setTaskDescription(new ActivityManager.TaskDescription());
+ final TaskInfo info = task.getTaskInfo();
+
+ // The intent of info should be a copy so assert that they are different instances.
+ Assert.assertThat(info.baseIntent, not(sameInstance(task.getBaseIntent())));
+ }
+
+ @Test
+ public void testReturnsToHomeRootTask() throws Exception {
+ final Task task = createTask(1);
+ spyOn(task);
+ doReturn(true).when(task).hasChild();
+ assertFalse(task.returnsToHomeRootTask());
+ task.intent = null;
+ assertFalse(task.returnsToHomeRootTask());
+ task.intent = new Intent();
+ assertFalse(task.returnsToHomeRootTask());
+ task.intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
+ assertTrue(task.returnsToHomeRootTask());
+ }
+
+ /** Ensures that empty bounds cause appBounds to inherit from parent. */
+ @Test
+ public void testAppBounds_EmptyBounds() {
+ final Rect emptyBounds = new Rect();
+ testRootTaskBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
+ mParentBounds);
+ }
+
+ /** Ensures that bounds on freeform root tasks are not clipped. */
+ @Test
+ public void testAppBounds_FreeFormBounds() {
+ final Rect freeFormBounds = new Rect(mParentBounds);
+ freeFormBounds.offset(10, 10);
+ testRootTaskBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
+ freeFormBounds);
+ }
+
+ /** Ensures that fully contained bounds are not clipped. */
+ @Test
+ public void testAppBounds_ContainedBounds() {
+ final Rect insetBounds = new Rect(mParentBounds);
+ insetBounds.inset(5, 5, 5, 5);
+ testRootTaskBoundsConfiguration(
+ WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds);
+ }
+
+ @Test
+ public void testFitWithinBounds() {
+ final Rect parentBounds = new Rect(10, 10, 200, 200);
+ TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
+ Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+ final Configuration parentConfig = rootTask.getConfiguration();
+ parentConfig.windowConfiguration.setBounds(parentBounds);
+ parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
+
+ // check top and left
+ Rect reqBounds = new Rect(-190, -190, 0, 0);
+ task.setBounds(reqBounds);
+ // Make sure part of it is exposed
+ assertTrue(task.getBounds().right > parentBounds.left);
+ assertTrue(task.getBounds().bottom > parentBounds.top);
+ // Should still be more-or-less in that corner
+ assertTrue(task.getBounds().left <= parentBounds.left);
+ assertTrue(task.getBounds().top <= parentBounds.top);
+
+ assertEquals(reqBounds.width(), task.getBounds().width());
+ assertEquals(reqBounds.height(), task.getBounds().height());
+
+ // check bottom and right
+ reqBounds = new Rect(210, 210, 400, 400);
+ task.setBounds(reqBounds);
+ // Make sure part of it is exposed
+ assertTrue(task.getBounds().left < parentBounds.right);
+ assertTrue(task.getBounds().top < parentBounds.bottom);
+ // Should still be more-or-less in that corner
+ assertTrue(task.getBounds().right >= parentBounds.right);
+ assertTrue(task.getBounds().bottom >= parentBounds.bottom);
+
+ assertEquals(reqBounds.width(), task.getBounds().width());
+ assertEquals(reqBounds.height(), task.getBounds().height());
+ }
+
+ /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
+ @Test
+ public void testBoundsOnModeChangeFreeformToFullscreen() {
+ DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
+ Task rootTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ Task task = rootTask.getBottomMostTask();
+ task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
+ DisplayInfo info = new DisplayInfo();
+ display.mDisplay.getDisplayInfo(info);
+ final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
+ final Rect freeformBounds = new Rect(fullScreenBounds);
+ freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+ (int) (freeformBounds.height() * 0.2));
+ task.setBounds(freeformBounds);
+
+ assertEquals(freeformBounds, task.getBounds());
+
+ // FULLSCREEN inherits bounds
+ rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertEquals(fullScreenBounds, task.getBounds());
+ assertEquals(freeformBounds, task.mLastNonFullscreenBounds);
+
+ // FREEFORM restores bounds
+ rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertEquals(freeformBounds, task.getBounds());
+ }
+
+ /**
+ * Tests that a task with forced orientation has orientation-consistent bounds within the
+ * parent.
+ */
+ @Test
+ public void testFullscreenBoundsForcedOrientation() {
+ final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+ final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm,
+ fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
+ assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
+ // Fix the display orientation to landscape which is the natural rotation (0) for the test
+ // display.
+ final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
+ dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+ final Task task = rootTask.getBottomMostTask();
+ final ActivityRecord root = task.getTopNonFinishingActivity();
+
+ assertEquals(fullScreenBounds, task.getBounds());
+
+ // Setting app to fixed portrait fits within parent
+ root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ assertEquals(root, task.getRootActivity());
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
+ // Portrait orientation is enforced on activity level. Task should fill fullscreen bounds.
+ assertThat(task.getBounds().height()).isLessThan(task.getBounds().width());
+ assertEquals(fullScreenBounds, task.getBounds());
+
+ // Top activity gets used
+ final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(rootTask)
+ .build();
+ assertEquals(top, task.getTopNonFinishingActivity());
+ top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
+ assertEquals(task.getBounds().width(), fullScreenBounds.width());
+
+ // Setting app to unspecified restores
+ top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
+ assertEquals(fullScreenBounds, task.getBounds());
+
+ // Setting app to fixed landscape and changing display
+ top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ // Fix the display orientation to portrait which is 90 degrees for the test display.
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
+
+ // Fixed orientation request should be resolved on activity level. Task fills display
+ // bounds.
+ assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
+ assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
+ assertEquals(fullScreenBoundsPort, task.getBounds());
+
+ // in FREEFORM, no constraint
+ final Rect freeformBounds = new Rect(display.getBounds());
+ freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+ (int) (freeformBounds.height() * 0.2));
+ rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.setBounds(freeformBounds);
+ assertEquals(freeformBounds, task.getBounds());
+
+ // FULLSCREEN letterboxes bounds on activity level, no constraint on task level.
+ rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
+ assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
+ assertEquals(fullScreenBoundsPort, task.getBounds());
+
+ // FREEFORM restores bounds as before
+ rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertEquals(freeformBounds, task.getBounds());
+ }
+
+ @Test
+ public void testReportsOrientationRequestInLetterboxForOrientation() {
+ final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+ final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm,
+ fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
+ assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
+ // Fix the display orientation to landscape which is the natural rotation (0) for the test
+ // display.
+ final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
+ dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+ final Task task = rootTask.getBottomMostTask();
+ ActivityRecord root = task.getTopNonFinishingActivity();
+
+ assertEquals(fullScreenBounds, task.getBounds());
+
+ // Setting app to fixed portrait fits within parent on activity level. Task fills parent.
+ root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ assertThat(root.getBounds().width()).isLessThan(root.getBounds().height());
+ assertEquals(task.getBounds(), fullScreenBounds);
+
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation());
+ }
+
+ @Test
+ public void testIgnoresForcedOrientationWhenParentHandles() {
+ final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+ DisplayContent display = new TestDisplayContent.Builder(
+ mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
+
+ display.getRequestedOverrideConfiguration().orientation =
+ Configuration.ORIENTATION_LANDSCAPE;
+ display.onRequestedOverrideConfigurationChanged(
+ display.getRequestedOverrideConfiguration());
+ Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+ Task task = rootTask.getBottomMostTask();
+ ActivityRecord root = task.getTopNonFinishingActivity();
+
+ final WindowContainer parentWindowContainer =
+ new WindowContainer(mSystemServicesTestRule.getWindowManagerService());
+ spyOn(parentWindowContainer);
+ parentWindowContainer.setBounds(fullScreenBounds);
+ doReturn(parentWindowContainer).when(task).getParent();
+ doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea();
+ doReturn(rootTask).when(task).getRootTask();
+ doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant();
+
+ // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the
+ // bounds because its parent says it will handle it at a later time.
+ root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ assertEquals(root, task.getRootActivity());
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
+ assertEquals(fullScreenBounds, task.getBounds());
+ }
+
+ @Test
+ public void testComputeConfigResourceOverrides() {
+ final Rect fullScreenBounds = new Rect(0, 0, 1080, 1920);
+ TestDisplayContent display = new TestDisplayContent.Builder(
+ mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Configuration inOutConfig = new Configuration();
+ final Configuration parentConfig = new Configuration();
+ final int longSide = 1200;
+ final int shortSide = 600;
+ final Rect parentBounds = new Rect(0, 0, 250, 500);
+ final Rect parentAppBounds = new Rect(0, 0, 250, 480);
+ parentConfig.windowConfiguration.setBounds(parentBounds);
+ parentConfig.windowConfiguration.setAppBounds(parentAppBounds);
+ parentConfig.densityDpi = 400;
+ parentConfig.screenHeightDp = (parentBounds.bottom * 160) / parentConfig.densityDpi; // 200
+ parentConfig.screenWidthDp = (parentBounds.right * 160) / parentConfig.densityDpi; // 100
+ parentConfig.windowConfiguration.setRotation(ROTATION_0);
+
+ // By default, the input bounds will fill parent.
+ task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+ assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp);
+ assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp);
+ assertEquals(parentAppBounds, inOutConfig.windowConfiguration.getAppBounds());
+ assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation);
+
+ // If bounds are overridden, config properties should be made to match. Surface hierarchy
+ // will crop for policy.
+ inOutConfig.setToDefaults();
+ final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide);
+ inOutConfig.windowConfiguration.setBounds(largerPortraitBounds);
+ task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+ // The override bounds are beyond the parent, the out appBounds should not be intersected
+ // by parent appBounds.
+ assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds());
+ assertEquals(longSide, inOutConfig.screenHeightDp * parentConfig.densityDpi / 160);
+ assertEquals(shortSide, inOutConfig.screenWidthDp * parentConfig.densityDpi / 160);
+
+ inOutConfig.setToDefaults();
+ // Landscape bounds.
+ final Rect largerLandscapeBounds = new Rect(0, 0, longSide, shortSide);
+ inOutConfig.windowConfiguration.setBounds(largerLandscapeBounds);
+
+ // Setup the display with a top stable inset. The later assertion will ensure the inset is
+ // excluded from screenHeightDp.
+ final int statusBarHeight = 100;
+ final DisplayPolicy policy = display.getDisplayPolicy();
+ doAnswer(invocationOnMock -> {
+ final Rect insets = invocationOnMock.<Rect>getArgument(0);
+ insets.top = statusBarHeight;
+ return null;
+ }).when(policy).convertNonDecorInsetsToStableInsets(any(), eq(ROTATION_0));
+
+ // Without limiting to be inside the parent bounds, the out screen size should keep relative
+ // to the input bounds.
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
+ final ActivityRecord.CompatDisplayInsets compatIntsets =
+ new ActivityRecord.CompatDisplayInsets(
+ display, activity, /* fixedOrientationBounds= */ null);
+ task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
+
+ assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
+ assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi,
+ inOutConfig.screenHeightDp);
+ assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi,
+ inOutConfig.screenWidthDp);
+ assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation);
+ }
+
+ @Test
+ public void testComputeConfigResourceLayoutOverrides() {
+ final Rect fullScreenBounds = new Rect(0, 0, 1000, 2500);
+ TestDisplayContent display = new TestDisplayContent.Builder(
+ mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Configuration inOutConfig = new Configuration();
+ final Configuration parentConfig = new Configuration();
+ final Rect nonLongBounds = new Rect(0, 0, 1000, 1250);
+ parentConfig.windowConfiguration.setBounds(fullScreenBounds);
+ parentConfig.windowConfiguration.setAppBounds(fullScreenBounds);
+ parentConfig.densityDpi = 400;
+ parentConfig.screenHeightDp = (fullScreenBounds.bottom * 160) / parentConfig.densityDpi;
+ parentConfig.screenWidthDp = (fullScreenBounds.right * 160) / parentConfig.densityDpi;
+ parentConfig.windowConfiguration.setRotation(ROTATION_0);
+
+ // Set BOTH screenW/H to an override value
+ inOutConfig.screenWidthDp = nonLongBounds.width() * 160 / parentConfig.densityDpi;
+ inOutConfig.screenHeightDp = nonLongBounds.height() * 160 / parentConfig.densityDpi;
+ task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+ // screenLayout should honor override when both screenW/H are set.
+ assertTrue((inOutConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_NO) != 0);
+ }
+
+ @Test
+ public void testComputeNestedConfigResourceOverrides() {
+ final Task task = new TaskBuilder(mSupervisor).build();
+ assertTrue(task.getResolvedOverrideBounds().isEmpty());
+ int origScreenH = task.getConfiguration().screenHeightDp;
+ Configuration rootTaskConfig = new Configuration();
+ rootTaskConfig.setTo(task.getRootTask().getRequestedOverrideConfiguration());
+ rootTaskConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ // Set bounds on root task (not task) and verify that the task resource configuration
+ // changes despite it's override bounds being empty.
+ Rect bounds = new Rect(task.getRootTask().getBounds());
+ bounds.bottom = (int) (bounds.bottom * 0.6f);
+ rootTaskConfig.windowConfiguration.setBounds(bounds);
+ task.getRootTask().onRequestedOverrideConfigurationChanged(rootTaskConfig);
+ assertNotEquals(origScreenH, task.getConfiguration().screenHeightDp);
+ }
+
+ @Test
+ public void testFullScreenTaskNotAdjustedByMinimalSize() {
+ final Task fullscreenTask = new TaskBuilder(mSupervisor).build();
+ final Rect originalTaskBounds = new Rect(fullscreenTask.getBounds());
+ final ActivityInfo aInfo = new ActivityInfo();
+ aInfo.windowLayout = new ActivityInfo.WindowLayout(0 /* width */, 0 /* widthFraction */,
+ 0 /* height */, 0 /* heightFraction */, 0 /* gravity */,
+ originalTaskBounds.width() * 2 /* minWidth */,
+ originalTaskBounds.height() * 2 /* minHeight */);
+ fullscreenTask.setMinDimensions(aInfo);
+ fullscreenTask.onConfigurationChanged(fullscreenTask.getParent().getConfiguration());
+
+ assertEquals(originalTaskBounds, fullscreenTask.getBounds());
+ }
+
+ @Test
+ public void testInsetDisregardedWhenFreeformOverlapsNavBar() {
+ TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
+ Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ DisplayInfo displayInfo = new DisplayInfo();
+ mAtm.mContext.getDisplay().getDisplayInfo(displayInfo);
+ final int displayHeight = displayInfo.logicalHeight;
+ final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+ final Configuration inOutConfig = new Configuration();
+ final Configuration parentConfig = new Configuration();
+ final int longSide = 1200;
+ final int shortSide = 600;
+ parentConfig.densityDpi = 400;
+ parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px
+ parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px
+ parentConfig.windowConfiguration.setRotation(ROTATION_0);
+
+ final int longSideDp = 480; // longSide / density = 1200 / 400 * 160
+ final int shortSideDp = 240; // shortSide / density = 600 / 400 * 160
+ final int screenLayout = parentConfig.screenLayout
+ & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
+ final int reducedScreenLayout =
+ Configuration.reduceScreenLayout(screenLayout, longSideDp, shortSideDp);
+
+ // Portrait bounds overlapping with navigation bar, without insets.
+ final Rect freeformBounds = new Rect(0,
+ displayHeight - 10 - longSide,
+ shortSide,
+ displayHeight - 10);
+ inOutConfig.windowConfiguration.setBounds(freeformBounds);
+ // Set to freeform mode to verify bug fix.
+ inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+ // screenW/H should not be effected by parent since overridden and freeform
+ assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
+ inOutConfig.screenWidthDp);
+ assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
+ inOutConfig.screenHeightDp);
+ assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
+
+ inOutConfig.setToDefaults();
+ // Landscape bounds overlapping with navigtion bar, without insets.
+ freeformBounds.set(0,
+ displayHeight - 10 - shortSide,
+ longSide,
+ displayHeight - 10);
+ inOutConfig.windowConfiguration.setBounds(freeformBounds);
+ inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+ assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
+ inOutConfig.screenWidthDp);
+ assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
+ inOutConfig.screenHeightDp);
+ assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
+ }
+
+ /** Ensures that the alias intent won't have target component resolved. */
+ @Test
+ public void testTaskIntentActivityAlias() {
+ final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
+ final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
+ final ComponentName aliasComponent =
+ new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName);
+ final ComponentName targetComponent =
+ new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
+
+ final Intent intent = new Intent();
+ intent.setComponent(aliasComponent);
+ final ActivityInfo info = new ActivityInfo();
+ info.applicationInfo = new ApplicationInfo();
+ info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
+ info.targetActivity = targetClassName;
+
+ final Task task = new Task.Builder(mAtm)
+ .setTaskId(1)
+ .setActivityInfo(info)
+ .setIntent(intent)
+ .build();
+ assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
+ task.intent.getComponent().getClassName());
+
+ ActivityRecord aliasActivity = new ActivityBuilder(mAtm).setComponent(
+ aliasComponent).setTargetActivity(targetClassName).build();
+ assertEquals("Should be the same intent filter.", true,
+ task.isSameIntentFilter(aliasActivity));
+
+ ActivityRecord targetActivity = new ActivityBuilder(mAtm).setComponent(
+ targetComponent).build();
+ assertEquals("Should be the same intent filter.", true,
+ task.isSameIntentFilter(targetActivity));
+
+ ActivityRecord defaultActivity = new ActivityBuilder(mAtm).build();
+ assertEquals("Should not be the same intent filter.", false,
+ task.isSameIntentFilter(defaultActivity));
+ }
+
+ /** Test that root activity index is reported correctly for several activities in the task. */
+ @Test
+ public void testFindRootIndex() {
+ final Task task = getTestTask();
+ // Add an extra activity on top of the root one
+ new ActivityBuilder(mAtm).setTask(task).build();
+
+ assertEquals("The root activity in the task must be reported.", task.getChildAt(0),
+ task.getRootActivity(
+ true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+ }
+
+ /**
+ * Test that root activity index is reported correctly for several activities in the task when
+ * the activities on the bottom are finishing.
+ */
+ @Test
+ public void testFindRootIndex_finishing() {
+ final Task task = getTestTask();
+ // Add extra two activities and mark the two on the bottom as finishing.
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ activity0.finishing = true;
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ activity1.finishing = true;
+ new ActivityBuilder(mAtm).setTask(task).build();
+
+ assertEquals("The first non-finishing activity in the task must be reported.",
+ task.getChildAt(2), task.getRootActivity(
+ true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+ }
+
+ /**
+ * Test that root activity index is reported correctly for several activities in the task when
+ * looking for the 'effective root'.
+ */
+ @Test
+ public void testFindRootIndex_effectiveRoot() {
+ final Task task = getTestTask();
+ // Add an extra activity on top of the root one
+ new ActivityBuilder(mAtm).setTask(task).build();
+
+ assertEquals("The root activity in the task must be reported.",
+ task.getChildAt(0), task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+ }
+
+ /**
+ * Test that root activity index is reported correctly when looking for the 'effective root' in
+ * case when bottom activities are relinquishing task identity or finishing.
+ */
+ @Test
+ public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() {
+ final Task task = getTestTask();
+ // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
+ // one above as finishing.
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ activity1.finishing = true;
+ new ActivityBuilder(mAtm).setTask(task).build();
+
+ assertEquals("The first non-finishing activity and non-relinquishing task identity "
+ + "must be reported.", task.getChildAt(2), task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+ }
+
+ /**
+ * Test that root activity index is reported correctly when looking for the 'effective root'
+ * for the case when there is only a single activity that also has relinquishTaskIdentity set.
+ */
+ @Test
+ public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() {
+ final Task task = getTestTask();
+ // Set relinquishTaskIdentity for the only activity in the task
+ task.getBottomMostActivity().info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+
+ assertEquals("The root activity in the task must be reported.",
+ task.getChildAt(0), task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+ }
+
+ /**
+ * Test that the topmost activity index is reported correctly when looking for the
+ * 'effective root' for the case when all activities have relinquishTaskIdentity set.
+ */
+ @Test
+ public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
+ final Task task = getTestTask();
+ // Set relinquishTaskIdentity for all activities in the task
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+
+ assertEquals("The topmost activity in the task must be reported.",
+ task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
+ }
+
+ /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
+ @Test
+ public void testGetRootActivity() {
+ final Task task = getTestTask();
+ // Add an extra activity on top of the root one
+ new ActivityBuilder(mAtm).setTask(task).build();
+
+ assertEquals("The root activity in the task must be reported.",
+ task.getBottomMostActivity(), task.getRootActivity());
+ }
+
+ /**
+ * Test that first non-finishing activity is reported in {@link Task#getRootActivity()}.
+ */
+ @Test
+ public void testGetRootActivity_finishing() {
+ final Task task = getTestTask();
+ // Add an extra activity on top of the root one
+ new ActivityBuilder(mAtm).setTask(task).build();
+ // Mark the root as finishing
+ task.getBottomMostActivity().finishing = true;
+
+ assertEquals("The first non-finishing activity in the task must be reported.",
+ task.getChildAt(1), task.getRootActivity());
+ }
+
+ /**
+ * Test that relinquishTaskIdentity flag is ignored in {@link Task#getRootActivity()}.
+ */
+ @Test
+ public void testGetRootActivity_relinquishTaskIdentity() {
+ final Task task = getTestTask();
+ // Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY.
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+ // Add an extra activity on top of the root one.
+ new ActivityBuilder(mAtm).setTask(task).build();
+
+ assertEquals("The root activity in the task must be reported.",
+ task.getBottomMostActivity(), task.getRootActivity());
+ }
+
+ /**
+ * Test that no activity is reported in {@link Task#getRootActivity()} when all activities
+ * in the task are finishing.
+ */
+ @Test
+ public void testGetRootActivity_allFinishing() {
+ final Task task = getTestTask();
+ // Mark the bottom-most activity as finishing.
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ activity0.finishing = true;
+ // Add an extra activity on top of the root one and mark it as finishing
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ activity1.finishing = true;
+
+ assertNull("No activity must be reported if all are finishing", task.getRootActivity());
+ }
+
+ /**
+ * Test that first non-finishing activity is the root of task.
+ */
+ @Test
+ public void testIsRootActivity() {
+ final Task task = getTestTask();
+ // Mark the bottom-most activity as finishing.
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ activity0.finishing = true;
+ // Add an extra activity on top of the root one.
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+
+ assertFalse("Finishing activity must not be the root of task", activity0.isRootOfTask());
+ assertTrue("Non-finishing activity must be the root of task", activity1.isRootOfTask());
+ }
+
+ /**
+ * Test that if all activities in the task are finishing, then the one on the bottom is the
+ * root of task.
+ */
+ @Test
+ public void testIsRootActivity_allFinishing() {
+ final Task task = getTestTask();
+ // Mark the bottom-most activity as finishing.
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ activity0.finishing = true;
+ // Add an extra activity on top of the root one and mark it as finishing
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ activity1.finishing = true;
+
+ assertTrue("Bottom activity must be the root of task", activity0.isRootOfTask());
+ assertFalse("Finishing activity on top must not be the root of task",
+ activity1.isRootOfTask());
+ }
+
+ /**
+ * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)}.
+ */
+ @Test
+ public void testGetTaskForActivity() {
+ final Task task0 = getTestTask();
+ final ActivityRecord activity0 = task0.getBottomMostActivity();
+
+ final Task task1 = getTestTask();
+ final ActivityRecord activity1 = task1.getBottomMostActivity();
+
+ assertEquals(task0.mTaskId,
+ ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
+ assertEquals(task1.mTaskId,
+ ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */));
+ }
+
+ /**
+ * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with finishing
+ * activity.
+ */
+ @Test
+ public void testGetTaskForActivity_onlyRoot_finishing() {
+ final Task task = getTestTask();
+ // Make the current root activity finishing
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ activity0.finishing = true;
+ // Add an extra activity on top - this will be the new root
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ // Add one more on top
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
+
+ assertEquals(task.mTaskId,
+ ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
+ assertEquals(task.mTaskId,
+ ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
+ assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
+ ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
+ }
+
+ /**
+ * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that
+ * relinquishes task identity.
+ */
+ @Test
+ public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
+ final Task task = getTestTask();
+ // Make the current root activity relinquish task identity
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+ // Add an extra activity on top - this will be the new root
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ // Add one more on top
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
+
+ assertEquals(task.mTaskId,
+ ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
+ assertEquals(task.mTaskId,
+ ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
+ assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
+ ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
+ }
+
+ /**
+ * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} allowing non-root
+ * entries.
+ */
+ @Test
+ public void testGetTaskForActivity_notOnlyRoot() {
+ final Task task = getTestTask();
+ // Mark the bottom-most activity as finishing.
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ activity0.finishing = true;
+
+ // Add an extra activity on top of the root one and make it relinquish task identity
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+
+ // Add one more activity on top
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
+
+ assertEquals(task.mTaskId,
+ ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
+ assertEquals(task.mTaskId,
+ ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */));
+ assertEquals(task.mTaskId,
+ ActivityRecord.getTaskForActivityLocked(activity2.appToken, false /* onlyRoot */));
+ }
+
+ /**
+ * Test {@link Task#updateEffectiveIntent()}.
+ */
+ @Test
+ public void testUpdateEffectiveIntent() {
+ // Test simple case with a single activity.
+ final Task task = getTestTask();
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+
+ spyOn(task);
+ task.updateEffectiveIntent();
+ verify(task).setIntent(eq(activity0));
+ }
+
+ /**
+ * Test {@link Task#updateEffectiveIntent()} with root activity marked as finishing. This
+ * should make the task use the second activity when updating the intent.
+ */
+ @Test
+ public void testUpdateEffectiveIntent_rootFinishing() {
+ // Test simple case with a single activity.
+ final Task task = getTestTask();
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ // Mark the bottom-most activity as finishing.
+ activity0.finishing = true;
+ // Add an extra activity on top of the root one
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+
+ spyOn(task);
+ task.updateEffectiveIntent();
+ verify(task).setIntent(eq(activity1));
+ }
+
+ /**
+ * Test {@link Task#updateEffectiveIntent()} when all activities are finishing or
+ * relinquishing task identity. In this case the root activity should still be used when
+ * updating the intent (legacy behavior).
+ */
+ @Test
+ public void testUpdateEffectiveIntent_allFinishing() {
+ // Test simple case with a single activity.
+ final Task task = getTestTask();
+ final ActivityRecord activity0 = task.getBottomMostActivity();
+ // Mark the bottom-most activity as finishing.
+ activity0.finishing = true;
+ // Add an extra activity on top of the root one and make it relinquish task identity
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
+ activity1.finishing = true;
+
+ // Task must still update the intent using the root activity (preserving legacy behavior).
+ spyOn(task);
+ task.updateEffectiveIntent();
+ verify(task).setIntent(eq(activity0));
+ }
+
+ @Test
+ public void testSaveLaunchingStateWhenConfigurationChanged() {
+ LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+ spyOn(persister);
+
+ final Task task = getTestTask();
+ task.setHasBeenVisible(false);
+ task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ task.setHasBeenVisible(true);
+ task.onConfigurationChanged(task.getParent().getConfiguration());
+
+ verify(persister).saveTask(task, task.getDisplayContent());
+ }
+
+ @Test
+ public void testSaveLaunchingStateWhenClearingParent() {
+ LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+ spyOn(persister);
+
+ final Task task = getTestTask();
+ task.setHasBeenVisible(false);
+ task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final DisplayContent oldDisplay = task.getDisplayContent();
+
+ LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams();
+ params.mWindowingMode = WINDOWING_MODE_UNDEFINED;
+ persister.getLaunchParams(task, null, params);
+ assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode);
+
+ task.setHasBeenVisible(true);
+ task.removeImmediately();
+
+ verify(persister).saveTask(task, oldDisplay);
+
+ persister.getLaunchParams(task, null, params);
+ assertEquals(WINDOWING_MODE_FULLSCREEN, params.mWindowingMode);
+ }
+
+ @Test
+ public void testNotSaveLaunchingStateNonFreeformDisplay() {
+ LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+ spyOn(persister);
+
+ final Task task = getTestTask();
+ task.setHasBeenVisible(false);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ task.setHasBeenVisible(true);
+ task.onConfigurationChanged(task.getParent().getConfiguration());
+
+ Mockito.verify(persister, never()).saveTask(same(task), any());
+ }
+
+ @Test
+ public void testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow() {
+ LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+ spyOn(persister);
+
+ final Task task = getTestTask();
+ task.setHasBeenVisible(false);
+ task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
+
+ task.setHasBeenVisible(true);
+ task.onConfigurationChanged(task.getParent().getConfiguration());
+
+ Mockito.verify(persister, never()).saveTask(same(task), any());
+ }
+
+ @Test
+ public void testNotSaveLaunchingStateForNonLeafTask() {
+ LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+ spyOn(persister);
+
+ final Task task = getTestTask();
+ task.setHasBeenVisible(false);
+ task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ final Task leafTask = createTaskInRootTask(task, 0 /* userId */);
+
+ leafTask.setHasBeenVisible(true);
+ task.setHasBeenVisible(true);
+ task.onConfigurationChanged(task.getParent().getConfiguration());
+
+ Mockito.verify(persister, never()).saveTask(same(task), any());
+ verify(persister).saveTask(same(leafTask), any());
+ }
+
+ @Test
+ public void testNotSpecifyOrientationByFloatingTask() {
+ final Task task = new TaskBuilder(mSupervisor)
+ .setCreateActivity(true).setCreateParentTask(true).build();
+ 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);
+
+ // TDA returns the last orientation when child returns UNSET
+ assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
+ }
+
+ @Test
+ public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() {
+ final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
+ FEATURE_VENDOR_FIRST);
+ final Task firstRootTask = firstTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final Task secondRootTask = secondTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+ .setTask(firstRootTask).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+ .setTask(secondRootTask).build();
+ firstActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ secondActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity on TDA1 is focused
+ mDisplayContent.setFocusedApp(firstActivity);
+
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
+
+ // No focused app, TDA1 is still recorded as last focused.
+ mDisplayContent.setFocusedApp(null);
+
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
+
+ // Activity on TDA2 is focused
+ mDisplayContent.setFocusedApp(secondActivity);
+
+ assertEquals(SCREEN_ORIENTATION_UNSET, firstTaskDisplayArea.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, secondTaskDisplayArea.getOrientation());
+ }
+
+ @Test
+ public void testNotifyOrientationChangeCausedByConfigurationChange() {
+ final Task task = getTestTask();
+ final ActivityRecord activity = task.getTopMostActivity();
+ final DisplayContent display = task.getDisplayContent();
+ display.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
+ verify(display).onDescendantOrientationChanged(same(task));
+ reset(display);
+
+ display.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation());
+ verify(display).onDescendantOrientationChanged(same(task));
+ }
+
+ private Task getTestTask() {
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ return task.getBottomMostTask();
+ }
+
+ private void testRootTaskBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
+ Rect expectedConfigBounds) {
+
+ TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
+ Task rootTask = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+
+ final Configuration parentConfig = rootTask.getConfiguration();
+ parentConfig.windowConfiguration.setAppBounds(parentBounds);
+ task.setBounds(bounds);
+
+ task.resolveOverrideConfiguration(parentConfig);
+ // Assert that both expected and actual are null or are equal to each other
+ assertEquals(expectedConfigBounds,
+ task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds());
+ }
+
+ private byte[] serializeToBytes(Task r) throws Exception {
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ final TypedXmlSerializer serializer = Xml.newFastSerializer();
+ serializer.setOutput(os, "UTF-8");
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TASK_TAG);
+ r.saveToXml(serializer);
+ serializer.endTag(null, TASK_TAG);
+ serializer.endDocument();
+
+ os.flush();
+ return os.toByteArray();
+ }
+ }
+
+ private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
+ try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(reader);
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(TASK_TAG, parser.getName());
+ return Task.restoreFromXml(parser, mAtm.mTaskSupervisor);
+ }
+ }
+
+ private Task createTask(int taskId) {
+ return new Task.Builder(mAtm)
+ .setTaskId(taskId)
+ .setIntent(new Intent())
+ .setRealActivity(ActivityBuilder.getDefaultComponent())
+ .setEffectiveUid(10050)
+ .buildInner();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 79ef868..2dfb3a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -69,10 +69,8 @@
ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
ArraySet<WindowContainer> participants = transition.mParticipants;
- final Task newTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
- final Task oldTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task newTask = createTask(mDisplayContent);
+ final Task oldTask = createTask(mDisplayContent);
final ActivityRecord closing = createActivityRecord(oldTask);
final ActivityRecord opening = createActivityRecord(newTask);
// Start states.
@@ -128,12 +126,10 @@
ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
ArraySet<WindowContainer> participants = transition.mParticipants;
- final Task newTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
- final Task newNestedTask = createTaskInStack(newTask, 0);
- final Task newNestedTask2 = createTaskInStack(newTask, 0);
- final Task oldTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task newTask = createTask(mDisplayContent);
+ final Task newNestedTask = createTaskInRootTask(newTask, 0);
+ final Task newNestedTask2 = createTaskInRootTask(newTask, 0);
+ final Task oldTask = createTask(mDisplayContent);
final ActivityRecord closing = createActivityRecord(oldTask);
final ActivityRecord opening = createActivityRecord(newNestedTask);
final ActivityRecord opening2 = createActivityRecord(newNestedTask2);
@@ -179,11 +175,9 @@
final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
ArraySet<WindowContainer> participants = transition.mParticipants;
- final Task showTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
- final Task showNestedTask = createTaskInStack(showTask, 0);
- final Task showTask2 = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task showTask = createTask(mDisplayContent);
+ final Task showNestedTask = createTaskInRootTask(showTask, 0);
+ final Task showTask2 = createTask(mDisplayContent);
final DisplayArea tda = showTask.getDisplayArea();
final ActivityRecord showing = createActivityRecord(showNestedTask);
final ActivityRecord showing2 = createActivityRecord(showTask2);
@@ -231,12 +225,10 @@
public void testCreateInfo_existenceChange() {
final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
- final Task openTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task openTask = createTask(mDisplayContent);
final ActivityRecord opening = createActivityRecord(openTask);
opening.mVisibleRequested = false; // starts invisible
- final Task closeTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task closeTask = createTask(mDisplayContent);
final ActivityRecord closing = createActivityRecord(closeTask);
closing.mVisibleRequested = true; // starts visible
@@ -268,8 +260,8 @@
final Task[] tasks = new Task[taskCount];
for (int i = 0; i < taskCount; ++i) {
// Each add goes on top, so at the end of this, task[9] should be on top
- tasks[i] = createTaskStackOnDisplay(WINDOWING_MODE_FREEFORM,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ tasks[i] = createTask(mDisplayContent,
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
final ActivityRecord act = createActivityRecord(tasks[i]);
// alternate so that the transition doesn't get promoted to the display area
act.mVisibleRequested = (i % 2) == 0; // starts invisible
@@ -305,8 +297,8 @@
final Task[] tasks = new Task[taskCount];
for (int i = 0; i < taskCount; ++i) {
// Each add goes on top, so at the end of this, task[9] should be on top
- tasks[i] = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ tasks[i] = createTask(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final ActivityRecord act = createActivityRecord(tasks[i]);
// alternate so that the transition doesn't get promoted to the display area
act.mVisibleRequested = (i % 2) == 0; // starts invisible
@@ -353,15 +345,13 @@
ArraySet<WindowContainer> participants = transition.mParticipants;
ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
- final Task openTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
- final Task openInOpenTask = createTaskInStack(openTask, 0);
+ final Task openTask = createTask(mDisplayContent);
+ final Task openInOpenTask = createTaskInRootTask(openTask, 0);
final ActivityRecord openInOpen = createActivityRecord(openInOpenTask);
- final Task changeTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
- final Task changeInChangeTask = createTaskInStack(changeTask, 0);
- final Task openInChangeTask = createTaskInStack(changeTask, 0);
+ final Task changeTask = createTask(mDisplayContent);
+ final Task changeInChangeTask = createTaskInRootTask(changeTask, 0);
+ final Task openInChangeTask = createTaskInRootTask(changeTask, 0);
final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask);
final ActivityRecord openInChange = createActivityRecord(openInChangeTask);
// set organizer for everything so that they all get added to transition info
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index b88173d..6919c4c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -811,18 +811,18 @@
@Test
public void testOnDisplayChanged() {
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
final DisplayContent newDc = createNewDisplay();
- stack.getDisplayArea().removeRootTask(stack);
- newDc.getDefaultTaskDisplayArea().addChild(stack, POSITION_TOP);
+ rootTask.getDisplayArea().removeRootTask(rootTask);
+ newDc.getDefaultTaskDisplayArea().addChild(rootTask, POSITION_TOP);
- verify(stack).onDisplayChanged(newDc);
+ verify(rootTask).onDisplayChanged(newDc);
verify(task).onDisplayChanged(newDc);
verify(activity).onDisplayChanged(newDc);
- assertEquals(newDc, stack.mDisplayContent);
+ assertEquals(newDc, rootTask.mDisplayContent);
assertEquals(newDc, task.mDisplayContent);
assertEquals(newDc, activity.mDisplayContent);
}
@@ -854,21 +854,21 @@
@Test
public void testTaskCanApplyAnimation() {
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task);
final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task);
verifyWindowContainerApplyAnimation(task, activity1, activity2);
}
@Test
- public void testStackCanApplyAnimation() {
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
+ public void testRootTaskCanApplyAnimation() {
+ final Task rootTask = createTask(mDisplayContent);
final ActivityRecord activity2 = createActivityRecord(mDisplayContent,
- createTaskInStack(stack, 0 /* userId */));
+ createTaskInRootTask(rootTask, 0 /* userId */));
final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
- createTaskInStack(stack, 0 /* userId */));
- verifyWindowContainerApplyAnimation(stack, activity1, activity2);
+ createTaskInRootTask(rootTask, 0 /* userId */));
+ verifyWindowContainerApplyAnimation(rootTask, activity1, activity2);
}
@Test
@@ -878,21 +878,21 @@
assertNull(windowContainer.getDisplayArea());
- // ActivityStack > WindowContainer
- final Task activityStack = createTaskStackOnDisplay(mDisplayContent);
- activityStack.addChild(windowContainer, 0);
- activityStack.setParent(null);
+ // Task > WindowContainer
+ final Task task = createTask(mDisplayContent);
+ task.addChild(windowContainer, 0);
+ task.setParent(null);
assertNull(windowContainer.getDisplayArea());
- assertNull(activityStack.getDisplayArea());
+ assertNull(task.getDisplayArea());
- // TaskDisplayArea > ActivityStack > WindowContainer
+ // TaskDisplayArea > Task > WindowContainer
final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(
mDisplayContent, mWm, "TaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
- taskDisplayArea.addChild(activityStack, 0);
+ taskDisplayArea.addChild(task, 0);
assertEquals(taskDisplayArea, windowContainer.getDisplayArea());
- assertEquals(taskDisplayArea, activityStack.getDisplayArea());
+ assertEquals(taskDisplayArea, task.getDisplayArea());
assertEquals(taskDisplayArea, taskDisplayArea.getDisplayArea());
// DisplayArea
@@ -986,8 +986,8 @@
@Test
public void testFreezeInsets() {
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
- final ActivityRecord activity = createActivityRecord(mDisplayContent, stack);
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
// Set visibility to false, verify the main window of the task will be set the frozen
@@ -1002,8 +1002,8 @@
@Test
public void testFreezeInsetsStateWhenAppTransition() {
- final Task stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 6d0e510..baf072d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -80,18 +80,18 @@
}
@Test
- public void testTaskFocusChange_stackNotHomeType_focusChanges() throws RemoteException {
+ public void testTaskFocusChange_rootTaskNotHomeType_focusChanges() throws RemoteException {
DisplayContent display = createNewDisplay();
// Current focused window
- Task focusedStack = createTaskStackOnDisplay(
- WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
- Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */);
+ Task focusedRootTask = createTask(
+ display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */);
WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
mDisplayContent.mCurrentFocus = focusedWindow;
// Tapped task
- Task tappedStack = createTaskStackOnDisplay(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, display);
- Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
+ Task tappedRootTask = createTask(
+ display, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
mWm.handleTaskFocusChange(tappedTask);
@@ -100,19 +100,19 @@
}
@Test
- public void testTaskFocusChange_stackHomeTypeWithSameTaskDisplayArea_focusDoesNotChange()
+ public void testTaskFocusChange_rootTaskHomeTypeWithSameTaskDisplayArea_focusDoesNotChange()
throws RemoteException {
DisplayContent display = createNewDisplay();
// Current focused window
- Task focusedStack = createTaskStackOnDisplay(
- WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
- Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */);
+ Task focusedRootTask = createTask(
+ display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */);
WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
mDisplayContent.mCurrentFocus = focusedWindow;
// Tapped home task
- Task tappedStack = createTaskStackOnDisplay(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, display);
- Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
+ Task tappedRootTask = createTask(
+ display, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
mWm.handleTaskFocusChange(tappedTask);
@@ -121,21 +121,21 @@
}
@Test
- public void testTaskFocusChange_stackHomeTypeWithDifferentTaskDisplayArea_focusChanges()
+ public void testTaskFocusChange_rootTaskHomeTypeWithDifferentTaskDisplayArea_focusChanges()
throws RemoteException {
final DisplayContent display = createNewDisplay();
final TaskDisplayArea secondTda = createTaskDisplayArea(
display, mWm, "Tapped TDA", FEATURE_VENDOR_FIRST);
// Current focused window
- Task focusedStack = createTaskStackOnDisplay(
- WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
- Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */);
+ Task focusedRootTask = createTask(
+ display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */);
WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
mDisplayContent.mCurrentFocus = focusedWindow;
// Tapped home task on another task display area
- Task tappedStack = createTaskStackOnTaskDisplayArea(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, secondTda);
- Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
+ Task tappedRootTask = createTask(secondTda, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD);
+ Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
mWm.handleTaskFocusChange(tappedTask);
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 01c503e..e2b58bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -125,8 +125,8 @@
return registerMockOrganizer(null);
}
- Task createTask(Task stack, boolean fakeDraw) {
- final Task task = createTaskInStack(stack, 0);
+ Task createTask(Task rootTask, boolean fakeDraw) {
+ final Task task = createTaskInRootTask(rootTask, 0);
if (fakeDraw) {
task.setHasBeenVisible(true);
@@ -134,13 +134,13 @@
return task;
}
- Task createTask(Task stack) {
+ Task createTask(Task rootTask) {
// Fake draw notifications for most of our tests.
- return createTask(stack, true);
+ return createTask(rootTask, true);
}
- Task createStack() {
- return createTaskStackOnDisplay(mDisplayContent);
+ Task createRootTask() {
+ return createTask(mDisplayContent);
}
@Before
@@ -153,14 +153,14 @@
@Test
public void testAppearVanish() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- stack.removeImmediately();
+ rootTask.removeImmediately();
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskVanished(any());
@@ -169,21 +169,21 @@
@Test
public void testAppearWaitsForVisibility() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack, false);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask, false);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- stack.setHasBeenVisible(true);
+ rootTask.setHasBeenVisible(true);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
- assertTrue(stack.getHasBeenVisible());
+ assertTrue(rootTask.getHasBeenVisible());
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- stack.removeImmediately();
+ rootTask.removeImmediately();
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskVanished(any());
@@ -192,108 +192,108 @@
@Test
public void testNoVanishedIfNoAppear() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack, false /* hasBeenVisible */);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask, false /* hasBeenVisible */);
// In this test we skip making the Task visible, and verify
// that even though a TaskOrganizer is set remove doesn't emit
// a vanish callback, because we never emitted appear.
- stack.setTaskOrganizer(organizer);
+ rootTask.setTaskOrganizer(organizer);
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- stack.removeImmediately();
+ rootTask.removeImmediately();
verify(organizer, never()).onTaskVanished(any());
}
@Test
public void testTaskNoDraw() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack, false /* fakeDraw */);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask, false /* fakeDraw */);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- assertTrue(stack.isOrganized());
+ assertTrue(rootTask.isOrganized());
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
- assertTaskVanished(organizer, false /* expectVanished */, stack);
- assertFalse(stack.isOrganized());
+ assertTaskVanished(organizer, false /* expectVanished */, rootTask);
+ assertFalse(rootTask.isOrganized());
}
@Test
public void testClearOrganizer() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- assertTrue(stack.isOrganized());
+ assertTrue(rootTask.isOrganized());
- stack.setTaskOrganizer(null);
+ rootTask.setTaskOrganizer(null);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskVanished(any());
- assertFalse(stack.isOrganized());
+ assertFalse(rootTask.isOrganized());
}
@Test
public void testUnregisterOrganizer() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- assertTrue(stack.isOrganized());
+ assertTrue(rootTask.isOrganized());
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
- assertTaskVanished(organizer, true /* expectVanished */, stack);
- assertFalse(stack.isOrganized());
+ assertTaskVanished(organizer, true /* expectVanished */, rootTask);
+ assertFalse(rootTask.isOrganized());
}
@Test
public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException {
- final Task stack = createStack();
- final Task task = createTask(stack);
- final Task stack2 = createStack();
- final Task task2 = createTask(stack2);
- final Task stack3 = createStack();
- final Task task3 = createTask(stack3);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
+ final Task rootTask2 = createRootTask();
+ final Task task2 = createTask(rootTask2);
+ final Task rootTask3 = createRootTask();
+ final Task task3 = createTask(rootTask3);
final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
// verify that tasks are returned and taskAppeared is not called
- assertContainsTasks(existingTasks, stack, stack2, stack3);
+ assertContainsTasks(existingTasks, rootTask, rootTask2, rootTask3);
verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
any(SurfaceControl.class));
verify(organizer, times(0)).onTaskVanished(any());
- assertTrue(stack.isOrganized());
+ assertTrue(rootTask.isOrganized());
// Now we replace the registration and verify the new organizer receives existing tasks
final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>();
final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
- assertContainsTasks(existingTasks2, stack, stack2, stack3);
+ assertContainsTasks(existingTasks2, rootTask, rootTask2, rootTask3);
verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
any(SurfaceControl.class));
verify(organizer2, times(0)).onTaskVanished(any());
// Removed tasks from the original organizer
- assertTaskVanished(organizer, true /* expectVanished */, stack, stack2, stack3);
- assertTrue(stack2.isOrganized());
+ assertTaskVanished(organizer, true /* expectVanished */, rootTask, rootTask2, rootTask3);
+ assertTrue(rootTask2.isOrganized());
// Now we unregister the second one, the first one should automatically be reregistered
// so we verify that it's now seeing changes.
@@ -302,18 +302,18 @@
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(3))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3);
+ assertTaskVanished(organizer2, true /* expectVanished */, rootTask, rootTask2, rootTask3);
}
@Test
public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
- final Task stack = createStack();
- final Task task = createTask(stack);
- final Task stack2 = createStack();
- final Task task2 = createTask(stack2);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
+ final Task rootTask2 = createRootTask();
+ final Task task2 = createTask(rootTask2);
ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
- assertContainsTasks(existingTasks, stack, stack2);
+ assertContainsTasks(existingTasks, rootTask, rootTask2);
// Verify we don't get onTaskAppeared if we are returned the tasks
verify(organizer, never())
@@ -323,21 +323,21 @@
@Test
public void testTaskTransaction() {
removeGlobalMinSizeRestriction();
- final Task stack = new TaskBuilder(mSupervisor)
+ final Task rootTask = new TaskBuilder(mSupervisor)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
- final Task task = stack.getTopMostTask();
+ final Task task = rootTask.getTopMostTask();
testTransaction(task);
}
@Test
- public void testStackTransaction() {
+ public void testRootTaskTransaction() {
removeGlobalMinSizeRestriction();
- final Task stack = new TaskBuilder(mSupervisor)
+ final Task rootTask = new TaskBuilder(mSupervisor)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
RootTaskInfo info =
mWm.mAtmService.getRootTaskInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
- assertEquals(stack.mRemoteToken.toWindowContainerToken(), info.token);
- testTransaction(stack);
+ assertEquals(rootTask.mRemoteToken.toWindowContainerToken(), info.token);
+ testTransaction(rootTask);
}
@Test
@@ -357,9 +357,9 @@
@Test
public void testSetWindowingMode() {
- final Task stack = new TaskBuilder(mSupervisor)
+ final Task rootTask = new TaskBuilder(mSupervisor)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
- testSetWindowingMode(stack);
+ testSetWindowingMode(rootTask);
final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea");
displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -376,30 +376,30 @@
@Test
public void testSetActivityWindowingMode() {
final ActivityRecord record = makePipableActivity();
- final Task stack = record.getRootTask();
+ final Task rootTask = record.getRootTask();
final WindowContainerTransaction t = new WindowContainerTransaction();
- t.setWindowingMode(stack.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_PINNED);
+ t.setWindowingMode(rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_PINNED);
t.setActivityWindowingMode(
- stack.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN);
+ rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode());
- assertEquals(WINDOWING_MODE_PINNED, stack.getWindowingMode());
+ assertEquals(WINDOWING_MODE_PINNED, rootTask.getWindowingMode());
}
@Test
public void testContainerFocusableChanges() {
removeGlobalMinSizeRestriction();
- final Task stack = new TaskBuilder(mSupervisor)
+ final Task rootTask = new TaskBuilder(mSupervisor)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
- final Task task = stack.getTopMostTask();
+ final Task task = rootTask.getTopMostTask();
WindowContainerTransaction t = new WindowContainerTransaction();
assertTrue(task.isFocusable());
- t.setFocusable(stack.mRemoteToken.toWindowContainerToken(), false);
+ t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), false);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
assertFalse(task.isFocusable());
- t.setFocusable(stack.mRemoteToken.toWindowContainerToken(), true);
+ t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), true);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
assertTrue(task.isFocusable());
}
@@ -407,25 +407,25 @@
@Test
public void testContainerHiddenChanges() {
removeGlobalMinSizeRestriction();
- final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
WindowContainerTransaction t = new WindowContainerTransaction();
- assertTrue(stack.shouldBeVisible(null));
- t.setHidden(stack.mRemoteToken.toWindowContainerToken(), true);
+ assertTrue(rootTask.shouldBeVisible(null));
+ t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), true);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
- assertFalse(stack.shouldBeVisible(null));
- t.setHidden(stack.mRemoteToken.toWindowContainerToken(), false);
+ assertFalse(rootTask.shouldBeVisible(null));
+ t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), false);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
- assertTrue(stack.shouldBeVisible(null));
+ assertTrue(rootTask.shouldBeVisible(null));
}
@Test
public void testSetIgnoreOrientationRequest_taskDisplayArea() {
removeGlobalMinSizeRestriction();
final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
- final Task stack = taskDisplayArea.createRootTask(
+ final Task rootTask = taskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
mDisplayContent.setFocusedApp(activity);
activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -461,9 +461,9 @@
public void testSetIgnoreOrientationRequest_displayContent() {
removeGlobalMinSizeRestriction();
final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
- final Task stack = taskDisplayArea.createRootTask(
+ final Task rootTask = taskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
mDisplayContent.setFocusedApp(activity);
activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -491,21 +491,21 @@
@Test
public void testOverrideConfigSize() {
removeGlobalMinSizeRestriction();
- final Task stack = new TaskBuilder(mSupervisor)
+ final Task rootTask = new TaskBuilder(mSupervisor)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
- final Task task = stack.getTopMostTask();
+ final Task task = rootTask.getTopMostTask();
WindowContainerTransaction t = new WindowContainerTransaction();
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
final int origScreenWDp = task.getConfiguration().screenHeightDp;
final int origScreenHDp = task.getConfiguration().screenHeightDp;
t = new WindowContainerTransaction();
// verify that setting config overrides on parent restricts children.
- t.setScreenSizeDp(stack.mRemoteToken
+ t.setScreenSizeDp(rootTask.mRemoteToken
.toWindowContainerToken(), origScreenWDp, origScreenHDp / 2);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
assertEquals(origScreenHDp / 2, task.getConfiguration().screenHeightDp);
t = new WindowContainerTransaction();
- t.setScreenSizeDp(stack.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED,
+ t.setScreenSizeDp(rootTask.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED,
SCREEN_HEIGHT_DP_UNDEFINED);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp);
@@ -572,14 +572,14 @@
mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
RunningTaskInfo info1 = task.getTaskInfo();
- final Task stack = createTaskStackOnDisplay(
- WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
- assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
+ final Task rootTask = createTask(
+ mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
+ assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode());
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reparent(stack.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
+ wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(info1.configuration.windowConfiguration.getWindowingMode(),
- stack.getWindowingMode());
+ rootTask.getWindowingMode());
// Info should reflect new membership
List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent);
@@ -591,14 +591,14 @@
Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
Configuration c = new Configuration(task1.getRequestedOverrideConfiguration());
c.windowConfiguration.setBounds(newSize);
- doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any(), any());
+ doNothing().when(rootTask).adjustForMinimalTaskDimensions(any(), any(), any());
task1.onRequestedOverrideConfigurationChanged(c);
- assertEquals(newSize, stack.getBounds());
+ assertEquals(newSize, rootTask.getBounds());
wct = new WindowContainerTransaction();
- wct.reparent(stack.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
+ wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
- assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
+ assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode());
infos = getTasksCreatedByOrganizer(mDisplayContent);
info1 = infos.get(0).getTaskInfo();
assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
@@ -644,36 +644,39 @@
lastReportedTiles.clear();
called[0] = false;
- final Task stack = createTaskStackOnDisplay(
- WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task rootTask = createTask(
+ mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reparent(stack.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
+ wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
lastReportedTiles.clear();
called[0] = false;
- final Task stack2 = createTaskStackOnDisplay(
- WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
+ final Task rootTask2 = createTask(
+ mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
wct = new WindowContainerTransaction();
- wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
+ wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
+ info1.token, true /* onTop */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType);
lastReportedTiles.clear();
called[0] = false;
- task1.positionChildAt(POSITION_TOP, stack, false /* includingParents */);
+ task1.positionChildAt(POSITION_TOP, rootTask, false /* includingParents */);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
lastReportedTiles.clear();
called[0] = false;
wct = new WindowContainerTransaction();
- wct.reparent(stack.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
- wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
+ wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
+ null, true /* onTop */);
+ wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
+ null, true /* onTop */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertTrue(called[0]);
assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
@@ -723,10 +726,10 @@
final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
mDisplayContent.mDisplayId, null /* activityTypes */).size();
- final Task stack = createTaskStackOnDisplay(
- WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
- final Task stack2 = createTaskStackOnDisplay(
- WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
+ final Task rootTask = createTask(
+ mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
+ final Task rootTask2 = createTask(
+ mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
// Check getRootTasks works
List<RunningTaskInfo> roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
@@ -735,8 +738,10 @@
lastReportedTiles.clear();
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reparent(stack.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
- wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), info2.token, true /* onTop */);
+ wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
+ info1.token, true /* onTop */);
+ wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
+ info2.token, true /* onTop */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertFalse(lastReportedTiles.isEmpty());
assertEquals(ACTIVITY_TYPE_STANDARD,
@@ -746,7 +751,8 @@
lastReportedTiles.clear();
wct = new WindowContainerTransaction();
- wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), info1.token, false /* onTop */);
+ wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
+ info1.token, false /* onTop */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertFalse(lastReportedTiles.isEmpty());
// Standard should still be on top of tile 1, so no change there
@@ -771,7 +777,7 @@
lastReportedTiles.clear();
wct = new WindowContainerTransaction();
- wct.reorder(stack2.mRemoteToken.toWindowContainerToken(), true /* onTop */);
+ wct.reorder(rootTask2.mRemoteToken.toWindowContainerToken(), true /* onTop */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
// Home should now be on top. No change occurs in second tile, so not reported
assertEquals(1, lastReportedTiles.size());
@@ -780,10 +786,10 @@
// This just needs to not crash (ie. it should be possible to reparent to display twice)
wct = new WindowContainerTransaction();
- wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
+ wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
wct = new WindowContainerTransaction();
- wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
+ wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
}
@@ -799,8 +805,8 @@
@Test
public void testBLASTCallbackWithActivityChildren() {
- final Task stackController1 = createStack();
- final Task task = createTask(stackController1);
+ final Task rootTaskController1 = createRootTask();
+ final Task task = createTask(rootTaskController1);
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
w.mActivityRecord.mVisibleRequested = true;
@@ -926,11 +932,11 @@
ChangeSavingOrganizer o = new ChangeSavingOrganizer();
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
- final Task stack = createStack();
- final Task task = createTask(stack);
- final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
+ final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
waitUntilHandlersIdle();
assertEquals("TestDescription", o.mChangedInfo.taskDescription.getLabel());
@@ -939,29 +945,29 @@
@Test
public void testPreventDuplicateAppear() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack, false /* fakeDraw */);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask, false /* fakeDraw */);
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- stack.setTaskOrganizer(organizer);
+ rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ rootTask.setTaskOrganizer(organizer);
// setHasBeenVisible was already called once by the set-up code.
- stack.setHasBeenVisible(true);
+ rootTask.setHasBeenVisible(true);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(1))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- stack.setTaskOrganizer(null);
+ rootTask.setTaskOrganizer(null);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(1)).onTaskVanished(any());
- stack.setTaskOrganizer(organizer);
+ rootTask.setTaskOrganizer(organizer);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(2))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- stack.removeImmediately();
+ rootTask.removeImmediately();
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(2)).onTaskVanished(any());
@@ -970,15 +976,15 @@
@Test
public void testInterceptBackPressedOnTaskRoot() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack);
- final ActivityRecord activity = createActivityRecord(stack.mDisplayContent, task);
- final Task stack2 = createStack();
- final Task task2 = createTask(stack2);
- final ActivityRecord activity2 = createActivityRecord(stack.mDisplayContent, task2);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
+ final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
+ final Task rootTask2 = createRootTask();
+ final Task task2 = createTask(rootTask2);
+ final ActivityRecord activity2 = createActivityRecord(rootTask.mDisplayContent, task2);
- assertTrue(stack.isOrganized());
- assertTrue(stack2.isOrganized());
+ assertTrue(rootTask.isOrganized());
+ assertTrue(rootTask2.isOrganized());
// Verify a back pressed does not call the organizer
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
@@ -989,7 +995,7 @@
// Enable intercepting back
mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(
- stack.mRemoteToken.toWindowContainerToken(), true);
+ rootTask.mRemoteToken.toWindowContainerToken(), true);
// Verify now that the back press does call the organizer
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
@@ -1000,7 +1006,7 @@
// Disable intercepting back
mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(
- stack.mRemoteToken.toWindowContainerToken(), false);
+ rootTask.mRemoteToken.toWindowContainerToken(), false);
// Verify now that the back press no longer calls the organizer
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
@@ -1012,8 +1018,8 @@
@Test
public void testBLASTCallbackWithWindows() throws Exception {
- final Task stackController = createStack();
- final Task task = createTask(stackController);
+ final Task rootTaskController = createRootTask();
+ final Task task = createTask(rootTaskController);
final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1");
final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2");
makeWindowVisible(w1);
@@ -1077,7 +1083,7 @@
final ITaskOrganizer organizer = registerMockOrganizer();
Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
- final Task task1 = createStack();
+ final Task task1 = createRootTask();
final Task task2 = createTask(rootTask, false /* fakeDraw */);
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reparent(task1.mRemoteToken.toWindowContainerToken(),
@@ -1090,19 +1096,19 @@
@Test
public void testAppearDeferThenInfoChange() {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
+ final Task rootTask = createRootTask();
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
- final Task task = createTask(stack);
- final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+ final Task task = createTask(rootTask);
+ final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType);
assertEquals("TestDescription",
@@ -1112,35 +1118,35 @@
@Test
public void testAppearDeferThenVanish() {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
+ final Task rootTask = createRootTask();
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
- final Task task = createTask(stack);
+ final Task task = createTask(rootTask);
- stack.removeImmediately();
+ rootTask.removeImmediately();
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
assertEquals(0, pendingEvents.size());
}
@Test
public void testInfoChangeDeferMultiple() {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack);
- final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
+ final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
assertEquals("TestDescription",
@@ -1149,7 +1155,7 @@
record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2"));
waitUntilHandlersIdle();
- pendingEvents = getTaskPendingEvent(stack);
+ pendingEvents = getTaskPendingEvent(rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
assertEquals("TestDescription2",
@@ -1159,20 +1165,20 @@
@Test
public void testInfoChangDeferThenVanish() {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack);
- final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
+ final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
- stack.removeImmediately();
+ rootTask.removeImmediately();
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
assertEquals("TestDescription",
@@ -1182,18 +1188,18 @@
@Test
public void testVanishDeferThenInfoChange() {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack);
- final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
+ final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
- stack.removeImmediately();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ rootTask.removeImmediately();
+ rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
}
@@ -1201,19 +1207,19 @@
@Test
public void testVanishDeferThenBackOnRoot() {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task stack = createStack();
- final Task task = createTask(stack);
- final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
+ final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
// Assume layout defer
mWm.mWindowPlacerLocked.deferLayout();
- stack.removeImmediately();
+ rootTask.removeImmediately();
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token,
new IRequestFinishCallback.Default());
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
}
@@ -1264,7 +1270,7 @@
@Test
public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
- final Task rootTask = createStack();
+ final Task rootTask = createRootTask();
final Task task = createTask(rootTask);
final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
final ArgumentCaptor<RunningTaskInfo> infoCaptor =
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 5b5b1da..bfbe203 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -249,21 +249,22 @@
assertFalse(appWindow.canBeImeTarget());
assertFalse(imeWindow.canBeImeTarget());
- // Simulate the window is in split screen primary stack and the current state is
- // minimized and home stack is resizable, so that we should ignore input for the stack.
+ // Simulate the window is in split screen primary root task and the current state is
+ // minimized and home root task is resizable, so that we should ignore input for the
+ // root task.
final DockedTaskDividerController controller =
mDisplayContent.getDockedDividerController();
- final Task stack = createTaskStackOnDisplay(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
- ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task rootTask = createTask(mDisplayContent,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
spyOn(appWindow);
spyOn(controller);
- spyOn(stack);
- stack.setFocusable(false);
- doReturn(stack).when(appWindow).getRootTask();
+ spyOn(rootTask);
+ rootTask.setFocusable(false);
+ doReturn(rootTask).when(appWindow).getRootTask();
// Make sure canBeImeTarget is false due to shouldIgnoreInput is true;
assertFalse(appWindow.canBeImeTarget());
- assertTrue(stack.shouldIgnoreInput());
+ assertTrue(rootTask.shouldIgnoreInput());
}
@Test
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 b210dfb..e9e0c99 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -429,35 +429,55 @@
return newTaskDisplayArea;
}
- /** Creates a {@link Task} and adds it to the specified {@link DisplayContent}. */
- Task createTaskStackOnDisplay(DisplayContent dc) {
- return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
+ /**
+ * Creates a {@link Task} with a simple {@link ActivityRecord} and adds to the given
+ * {@link TaskDisplayArea}.
+ */
+ Task createTaskWithActivity(TaskDisplayArea taskDisplayArea,
+ int windowingMode, int activityType, boolean onTop, boolean twoLevelTask) {
+ return createTask(taskDisplayArea, windowingMode, activityType,
+ onTop, true /* createActivity */, twoLevelTask);
}
- Task createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) {
- return new TaskBuilder(dc.mAtmService.mTaskSupervisor)
- .setDisplay(dc)
+ /** Creates a {@link Task} and adds to the given {@link DisplayContent}. */
+ Task createTask(DisplayContent dc) {
+ return createTask(dc.getDefaultTaskDisplayArea(),
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ }
+
+ Task createTask(DisplayContent dc, int windowingMode, int activityType) {
+ return createTask(dc.getDefaultTaskDisplayArea(), windowingMode, activityType);
+ }
+
+ Task createTask(TaskDisplayArea taskDisplayArea, int windowingMode, int activityType) {
+ return createTask(taskDisplayArea, windowingMode, activityType,
+ true /* onTop */, false /* createActivity */, false /* twoLevelTask */);
+ }
+
+ /** Creates a {@link Task} and adds to the given {@link TaskDisplayArea}. */
+ Task createTask(TaskDisplayArea taskDisplayArea, int windowingMode, int activityType,
+ boolean onTop, boolean createActivity, boolean twoLevelTask) {
+ final TaskBuilder builder = new TaskBuilder(mSupervisor)
+ .setTaskDisplayArea(taskDisplayArea)
.setWindowingMode(windowingMode)
.setActivityType(activityType)
- .setIntent(new Intent())
- .build();
+ .setOnTop(onTop)
+ .setCreateActivity(createActivity);
+ if (twoLevelTask) {
+ return builder
+ .setCreateParentTask(true)
+ .build()
+ .getRootTask();
+ } else {
+ return builder.build();
+ }
}
- Task createTaskStackOnTaskDisplayArea(int windowingMode, int activityType,
- TaskDisplayArea tda) {
- return new TaskBuilder(tda.mDisplayContent.mAtmService.mTaskSupervisor)
- .setTaskDisplayArea(tda)
- .setWindowingMode(windowingMode)
- .setActivityType(activityType)
- .setIntent(new Intent())
- .build();
- }
-
- /** Creates a {@link Task} and adds it to the specified {@link Task}. */
- Task createTaskInStack(Task stack, int userId) {
- final Task task = new TaskBuilder(stack.mTaskSupervisor)
+ /** Creates a {@link Task} and adds to the given root {@link Task}. */
+ Task createTaskInRootTask(Task rootTask, int userId) {
+ final Task task = new TaskBuilder(rootTask.mTaskSupervisor)
.setUserId(userId)
- .setParentTask(stack)
+ .setParentTask(rootTask)
.build();
return task;
}
@@ -485,7 +505,7 @@
*/
ActivityRecord createActivityRecord(DisplayContent dc, int windowingMode,
int activityType) {
- final Task task = createTaskStackOnDisplay(windowingMode, activityType, dc);
+ final Task task = createTask(dc, windowingMode, activityType);
return createActivityRecord(dc, task);
}
@@ -517,7 +537,7 @@
*/
ActivityRecord createActivityRecordWithParentTask(DisplayContent dc, int windowingMode,
int activityType) {
- final Task task = createTaskStackOnDisplay(windowingMode, activityType, dc);
+ final Task task = createTask(dc, windowingMode, activityType);
return createActivityRecordWithParentTask(task);
}
@@ -871,7 +891,7 @@
.setParentTask(mParentTask).build();
} else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateRootTask(
mParentTask.getWindowingMode(), mParentTask.getActivityType())) {
- // The stack can be the task root.
+ // The parent task can be the task root.
mTask = mParentTask;
}
@@ -921,9 +941,9 @@
doReturn(true).when(activity).fillsParent();
mTask.addChild(activity);
if (mOnTop) {
- // Move the task to front after activity added.
- // Or {@link TaskDisplayArea#mPreferredTopFocusableStack} could be other stacks
- // (e.g. home stack).
+ // Move the task to front after activity is added.
+ // Or {@link TaskDisplayArea#mPreferredTopFocusableRootTask} could be other
+ // root tasks (e.g. home root task).
mTask.moveToFront("createActivity");
}
// Make visible by default...
@@ -1127,8 +1147,8 @@
.build();
if (mOnTop) {
// We move the task to front again in order to regain focus after activity
- // added to the stack. Or {@link TaskDisplayArea#mPreferredTopFocusableStack}
- // could be other stacks (e.g. home stack).
+ // is added. Or {@link TaskDisplayArea#mPreferredTopFocusableRootTask} could be
+ // other root tasks (e.g. home root task).
task.moveToFront("createActivityTask");
} else {
task.moveToBack("createActivityTask", null);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index afa35fe..2e692e6 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -917,20 +917,31 @@
boolean prevHostConnected = mHostConnected;
UsbPort port = (UsbPort) args.arg1;
UsbPortStatus status = (UsbPortStatus) args.arg2;
- mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
- mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
- mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
- mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+
+ if (status != null) {
+ mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST;
+ mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE;
+ mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK;
+ mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY);
+
+ // Ideally we want to see if PR_SWAP and DR_SWAP is supported.
+ // But, this should be suffice, since, all four combinations are only supported
+ // when PR_SWAP and DR_SWAP are supported.
+ mSupportsAllCombinations = status.isRoleCombinationSupported(
+ POWER_ROLE_SOURCE, DATA_ROLE_HOST)
+ && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
+ && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
+ DATA_ROLE_DEVICE)
+ && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE);
+ } else {
+ mHostConnected = false;
+ mSourcePower = false;
+ mSinkPower = false;
+ mAudioAccessoryConnected = false;
+ mSupportsAllCombinations = false;
+ }
+
mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY);
- // Ideally we want to see if PR_SWAP and DR_SWAP is supported.
- // But, this should be suffice, since, all four combinations are only supported
- // when PR_SWAP and DR_SWAP are supported.
- mSupportsAllCombinations = status.isRoleCombinationSupported(
- POWER_ROLE_SOURCE, DATA_ROLE_HOST)
- && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)
- && status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
- DATA_ROLE_DEVICE)
- && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE);
args.recycle();
updateUsbNotification(false);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 6541774..5d541e9 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -16,6 +16,9 @@
package com.android.server.voiceinteraction;
+import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL;
+import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -24,23 +27,30 @@
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionService;
import android.service.voice.HotwordRejectedResult;
import android.service.voice.IDspHotwordDetectionCallback;
import android.service.voice.IHotwordDetectionService;
+import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.infra.ServiceConnector;
+import java.io.Closeable;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -63,6 +73,8 @@
private static final long VALIDATION_TIMEOUT_MILLIS = 3000;
private static final long VOICE_INTERACTION_TIMEOUT_TO_OPEN_MIC_MILLIS = 2000;
private static final int MAX_STREAMING_SECONDS = 10;
+ private static final int MICROPHONE_BUFFER_LENGTH_SECONDS = 8;
+ private static final int HOTWORD_AUDIO_LENGTH_SECONDS = 3;
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
// TODO: This may need to be a Handler(looper)
@@ -76,9 +88,12 @@
final @NonNull ServiceConnector<IHotwordDetectionService> mRemoteHotwordDetectionService;
boolean mBound;
+ @GuardedBy("mLock")
+ private ParcelFileDescriptor mCurrentAudioSink;
+
HotwordDetectionConnection(Object lock, Context context, ComponentName serviceName,
int userId, boolean bindInstantServiceAllowed, @Nullable PersistableBundle options,
- @Nullable SharedMemory sharedMemory) {
+ @Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
mLock = lock;
mContext = context;
mDetectionComponentName = serviceName;
@@ -96,10 +111,10 @@
mBound = connected;
if (connected) {
try {
- service.setConfig(options, sharedMemory);
+ service.updateState(options, sharedMemory, callback);
} catch (RemoteException e) {
// TODO: (b/181842909) Report an error to voice interactor
- Slog.w(TAG, "Failed to setConfig for HotwordDetectionService", e);
+ Slog.w(TAG, "Failed to updateState for HotwordDetectionService", e);
}
}
}
@@ -129,9 +144,65 @@
}
}
- void setConfigLocked(PersistableBundle options, SharedMemory sharedMemory) {
+ void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory) {
mRemoteHotwordDetectionService.run(
- service -> service.setConfig(options, sharedMemory));
+ service -> service.updateState(options, sharedMemory, null /* callback */));
+ }
+
+ void startListeningFromMic(
+ AudioFormat audioFormat,
+ IMicrophoneHotwordDetectionVoiceInteractionCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningFromMic");
+ }
+
+ AudioRecord audioRecord = createMicAudioRecord(audioFormat);
+ if (audioRecord == null) {
+ // TODO: Callback.onError();
+ return;
+ }
+
+ handleSoftwareHotwordDetection(
+ audioFormat,
+ AudioReader.createFromAudioRecord(audioRecord),
+ AUDIO_SOURCE_MICROPHONE,
+ // TODO: handle bundles better.
+ new PersistableBundle(),
+ callback);
+ }
+
+ public void startListeningFromExternalSource(
+ ParcelFileDescriptor audioStream,
+ AudioFormat audioFormat,
+ @Nullable PersistableBundle options,
+ IMicrophoneHotwordDetectionVoiceInteractionCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningFromExternalSource");
+ }
+
+ ParcelFileDescriptor.AutoCloseInputStream audioReader =
+ new ParcelFileDescriptor.AutoCloseInputStream(audioStream);
+
+ handleSoftwareHotwordDetection(
+ audioFormat,
+ AudioReader.createFromInputStream(audioReader),
+ AUDIO_SOURCE_EXTERNAL,
+ options,
+ callback);
+ }
+
+ void stopListening() {
+ if (DEBUG) {
+ Slog.d(TAG, "stopListening");
+ }
+
+ synchronized (mLock) {
+ if (mCurrentAudioSink != null) {
+ Slog.i(TAG, "Closing audio stream to hotword detector: stopping requested");
+ bestEffortClose(mCurrentAudioSink);
+ }
+ mCurrentAudioSink = null;
+ }
}
private void detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent,
@@ -175,7 +246,7 @@
Runnable cancellingJob = () -> {
record.stop();
- bestEffortCloseFileDescriptor(audioSink);
+ bestEffortClose(audioSink);
// TODO: consider calling externalCallback.onRejected(ERROR_TIMEOUT).
};
@@ -186,11 +257,11 @@
// TODO: consider making this a non-anonymous class.
IDspHotwordDetectionCallback internalCallback = new IDspHotwordDetectionCallback.Stub() {
@Override
- public void onDetected() throws RemoteException {
+ public void onDetected(HotwordDetectedResult result) throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "onDetected");
}
- bestEffortCloseFileDescriptor(audioSink);
+ bestEffortClose(audioSink);
cancelingFuture.cancel(true);
// Give 2 more seconds for the interactor to start consuming the mic. If it fails to
@@ -202,6 +273,7 @@
VOICE_INTERACTION_TIMEOUT_TO_OPEN_MIC_MILLIS,
TimeUnit.MILLISECONDS);
+ // TODO: Propagate the HotwordDetectedResult.
externalCallback.onKeyphraseDetected(recognitionEvent);
}
@@ -221,7 +293,7 @@
recognitionEvent.getCaptureFormat(),
VALIDATION_TIMEOUT_MILLIS,
internalCallback));
- bestEffortCloseFileDescriptor(clientRead);
+ bestEffortClose(clientRead);
}
static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub {
@@ -283,16 +355,48 @@
new AudioAttributes.Builder()
.setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build(),
recognitionEvent.getCaptureFormat(),
- getBufferSizeInBytes(sampleRate, MAX_STREAMING_SECONDS),
+ getBufferSizeInBytes(
+ sampleRate,
+ MAX_STREAMING_SECONDS,
+ recognitionEvent.getCaptureFormat().getChannelCount()),
recognitionEvent.getCaptureSession());
}
+ @Nullable
+ private AudioRecord createMicAudioRecord(AudioFormat audioFormat) {
+ if (DEBUG) {
+ Slog.i(TAG, "#createAudioRecord");
+ }
+ try {
+ AudioRecord audioRecord = new AudioRecord(
+ new AudioAttributes.Builder().setHotwordMode().build(),
+ audioFormat,
+ getBufferSizeInBytes(
+ audioFormat.getSampleRate(),
+ MICROPHONE_BUFFER_LENGTH_SECONDS,
+ audioFormat.getChannelCount()),
+ AudioManager.AUDIO_SESSION_ID_GENERATE);
+
+ if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
+ Slog.w(TAG, "Failed to initialize AudioRecord");
+ audioRecord.release();
+ return null;
+ }
+
+ return audioRecord;
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Failed to create AudioRecord", e);
+ return null;
+ }
+ }
+
/**
* Returns the number of bytes required to store {@code bufferLengthSeconds} of audio sampled at
* {@code sampleRate} Hz, using the format returned by DSP audio capture.
*/
- private static int getBufferSizeInBytes(int sampleRate, int bufferLengthSeconds) {
- return BYTES_PER_SAMPLE * sampleRate * bufferLengthSeconds;
+ private static int getBufferSizeInBytes(
+ int sampleRate, int bufferLengthSeconds, int intChannelCount) {
+ return BYTES_PER_SAMPLE * sampleRate * bufferLengthSeconds * intChannelCount;
}
private static Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() {
@@ -307,17 +411,176 @@
return Pair.create(fileDescriptors[0], fileDescriptors[1]);
}
- private static void bestEffortCloseFileDescriptor(ParcelFileDescriptor fd) {
- try {
- fd.close();
- } catch (IOException e) {
- if (DEBUG) {
- Slog.w(TAG, "Failed closing file descriptor", e);
- }
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mBound="); pw.println(mBound);
+ }
+
+ private interface AudioReader extends Closeable {
+ int read(byte[] dest, int offset, int length) throws IOException;
+
+ static AudioReader createFromInputStream(InputStream is) {
+ return new AudioReader() {
+ @Override
+ public int read(byte[] dest, int offset, int length) throws IOException {
+ return is.read(dest, offset, length);
+ }
+
+ @Override
+ public void close() throws IOException {
+ is.close();
+ }
+ };
+ }
+
+ static AudioReader createFromAudioRecord(AudioRecord record) {
+ record.startRecording();
+
+ return new AudioReader() {
+ @Override
+ public int read(byte[] dest, int offset, int length) throws IOException {
+ return record.read(dest, offset, length);
+ }
+
+ @Override
+ public void close() throws IOException {
+ record.stop();
+ record.release();
+ }
+ };
}
}
- public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("mBound="); pw.println(mBound);
+ private void handleSoftwareHotwordDetection(
+ AudioFormat audioFormat,
+ AudioReader audioSource,
+ int audioSourceValue,
+ @Nullable PersistableBundle options,
+ IMicrophoneHotwordDetectionVoiceInteractionCallback callback) {
+ Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
+
+ if (clientPipe == null) {
+ // TODO: Need to propagate as unknown error or something?
+ return;
+ }
+ ParcelFileDescriptor serviceAudioSink = clientPipe.second;
+ ParcelFileDescriptor serviceAudioSource = clientPipe.first;
+
+ synchronized (mLock) {
+ mCurrentAudioSink = serviceAudioSink;
+ }
+
+ mAudioCopyExecutor.execute(() -> {
+ try (AudioReader source = audioSource;
+ OutputStream fos =
+ new ParcelFileDescriptor.AutoCloseOutputStream(serviceAudioSink)) {
+ byte[] buffer = new byte[1024];
+
+ while (true) {
+ int bytesRead = source.read(buffer, 0, 1024);
+
+ if (bytesRead < 0) {
+ break;
+ }
+
+ // TODO: First write to ring buffer to make sure we don't lose data if the next
+ // statement fails.
+ // ringBuffer.append(buffer, bytesRead);
+ fos.write(buffer, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed supplying audio data to validator", e);
+ } finally {
+ synchronized (mLock) {
+ mCurrentAudioSink = null;
+ }
+ }
+ });
+
+ // TODO: handle cancellations well
+ // TODO: what if we cancelled and started a new one?
+ mRemoteHotwordDetectionService.run(
+ service -> service.detectFromMicrophoneSource(
+ serviceAudioSource,
+ // TODO: consider making a proxy callback + copy of audio format
+ audioSourceValue, audioFormat, options,
+ new IDspHotwordDetectionCallback.Stub() {
+ @Override
+ public void onRejected(@Nullable HotwordRejectedResult result)
+ throws RemoteException {
+ // TODO: Propagate the HotwordRejectedResult.
+ }
+
+ @Override
+ public void onDetected(@Nullable HotwordDetectedResult triggerResult)
+ throws RemoteException {
+ // Stop
+ bestEffortClose(serviceAudioSink);
+
+ if (audioSourceValue == AUDIO_SOURCE_EXTERNAL) {
+ callback.onDetected(triggerResult, null, null);
+ // TODO: Add a delay before closing.
+ bestEffortClose(audioSource);
+ }
+
+ Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe =
+ createPipe();
+
+ if (clientPipe == null) {
+ // Error.
+ // Need to propagate as unknown error or something?
+ return;
+ }
+ ParcelFileDescriptor callbackAudioSink = clientPipe.second;
+ ParcelFileDescriptor callbackClientRead = clientPipe.first;
+
+ mAudioCopyExecutor.execute(() -> {
+ try (AudioReader source = audioSource;
+ OutputStream fos =
+ new ParcelFileDescriptor.AutoCloseOutputStream(
+ callbackAudioSink)) {
+
+ // TODO: get ring buffer suffix here
+ // fos.write(lastSeveralSeconds);
+
+ byte[] buffer = new byte[1024];
+ while (true) {
+ int bytesRead = source.read(buffer, 0, 1024);
+
+ if (bytesRead < 0) {
+ break;
+ }
+
+ fos.write(buffer, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed supplying audio data to validator", e);
+ } finally {
+ synchronized (mLock) {
+ mCurrentAudioSink = null;
+ }
+ }
+ });
+
+ // TODO: noteOp here.
+ // Remove hotword offset from trigger result
+ // TODO: consider propagating only capture session.
+ callback.onDetected(triggerResult, null, callbackClientRead);
+ // TODO: Add a delay before closing.
+ bestEffortClose(callbackClientRead);
+ }
+ }));
+
+ // TODO: verify this is the right thing to do here.
+ bestEffortClose(serviceAudioSource);
+ }
+
+ private static void bestEffortClose(Closeable closeable) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "Failed closing", e);
+ }
+ }
}
};
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/RecognitionServiceInfo.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/RecognitionServiceInfo.java
new file mode 100644
index 0000000..05d4b93
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/RecognitionServiceInfo.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.voiceinteraction;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.speech.RecognitionService;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+// TODO: Move this class somewhere else, along with the default recognizer logic in
+// VoiceInteractionManagerService.
+// TODO: Use this class in com.android.settings.applications.assist.VoiceInputHelper.
+
+/**
+ * {@link ServiceInfo} and parsed metadata for a {@link RecognitionService}.
+ */
+class RecognitionServiceInfo {
+ private static final String TAG = "RecognitionServiceInfo";
+
+ private final String mParseError;
+ private final ServiceInfo mServiceInfo;
+ private final boolean mSelectableAsDefault;
+
+ /**
+ * Queries the valid recognition services available for the user.
+ */
+ static List<RecognitionServiceInfo> getAvailableServices(
+ @NonNull Context context, @UserIdInt int user) {
+ List<RecognitionServiceInfo> services = new ArrayList<>();
+
+ List<ResolveInfo> resolveInfos =
+ context.getPackageManager().queryIntentServicesAsUser(
+ new Intent(RecognitionService.SERVICE_INTERFACE),
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ user);
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ RecognitionServiceInfo service =
+ parseInfo(context.getPackageManager(), resolveInfo.serviceInfo);
+ if (!TextUtils.isEmpty(service.mParseError)) {
+ Log.w(TAG, "Parse error in getAvailableServices: " + service.mParseError);
+ // We still use the recognizer to preserve pre-existing behavior.
+ }
+ services.add(service);
+ }
+ return services;
+ }
+
+ /**
+ * Loads the service metadata published by the component. Success is indicated by {@link
+ * #getParseError()}.
+ *
+ * @param pm A PackageManager from which the XML can be loaded; usually the
+ * PackageManager from which {@code si} was originally retrieved.
+ * @param si The {@link android.speech.RecognitionService} info.
+ */
+ static RecognitionServiceInfo parseInfo(@NonNull PackageManager pm, @NonNull ServiceInfo si) {
+ String parseError = "";
+ boolean selectableAsDefault = true; // default
+ try (XmlResourceParser parser = si.loadXmlMetaData(
+ pm,
+ RecognitionService.SERVICE_META_DATA)) {
+ if (parser == null) {
+ parseError = "No " + RecognitionService.SERVICE_META_DATA
+ + " meta-data for " + si.packageName;
+ return new RecognitionServiceInfo(si, selectableAsDefault, parseError);
+ }
+ Resources res = pm.getResourcesForApplication(si.applicationInfo);
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type = 0;
+ while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+ type = parser.next();
+ }
+
+ String nodeName = parser.getName();
+ if (!"recognition-service".equals(nodeName)) {
+ throw new XmlPullParserException(
+ "Meta-data does not start with recognition-service tag");
+ }
+
+ TypedArray values =
+ res.obtainAttributes(
+ attrs, com.android.internal.R.styleable.RecognitionService);
+ selectableAsDefault =
+ values.getBoolean(
+ com.android.internal.R.styleable.RecognitionService_selectableAsDefault,
+ selectableAsDefault);
+ values.recycle();
+ } catch (XmlPullParserException | IOException | PackageManager.NameNotFoundException e) {
+ parseError = "Error parsing recognition service meta-data: " + e;
+ }
+ return new RecognitionServiceInfo(si, selectableAsDefault, parseError);
+ }
+
+ private RecognitionServiceInfo(
+ @NonNull ServiceInfo si, boolean selectableAsDefault, @NonNull String parseError) {
+ mServiceInfo = si;
+ mSelectableAsDefault = selectableAsDefault;
+ mParseError = parseError;
+ }
+
+ @NonNull
+ public String getParseError() {
+ return mParseError;
+ }
+
+ @NonNull
+ public ServiceInfo getServiceInfo() {
+ return mServiceInfo;
+ }
+
+ public boolean isSelectableAsDefault() {
+ return mSelectableAsDefault;
+ }
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 29354eb..ccaeaf9 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -48,6 +48,7 @@
import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.media.AudioFormat;
import android.media.permission.Identity;
import android.media.permission.PermissionUtil;
import android.media.permission.SafeCloseable;
@@ -56,6 +57,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
@@ -66,12 +68,12 @@
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo;
import android.service.voice.VoiceInteractionSession;
-import android.speech.RecognitionService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -103,6 +105,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -404,9 +407,30 @@
ComponentName curInteractor = !TextUtils.isEmpty(curInteractorStr)
? ComponentName.unflattenFromString(curInteractorStr) : null;
try {
- recognizerInfo = pm.getServiceInfo(curRecognizer,
+ recognizerInfo = pm.getServiceInfo(
+ curRecognizer,
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.GET_META_DATA,
+ userHandle);
+ if (recognizerInfo != null) {
+ RecognitionServiceInfo rsi =
+ RecognitionServiceInfo.parseInfo(
+ mContext.getPackageManager(), recognizerInfo);
+ if (!TextUtils.isEmpty(rsi.getParseError())) {
+ Log.w(TAG, "Parse error in getAvailableServices: "
+ + rsi.getParseError());
+ // We still use the recognizer to preserve pre-existing behavior.
+ }
+ if (!rsi.isSelectableAsDefault()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Found non selectableAsDefault recognizer as"
+ + " default. Unsetting the default and looking for another"
+ + " one.");
+ }
+ recognizerInfo = null;
+ }
+ }
if (curInteractor != null) {
interactorInfo = pm.getServiceInfo(curInteractor,
PackageManager.MATCH_DIRECT_BOOT_AWARE
@@ -650,19 +674,23 @@
prefPackage = getDefaultRecognizer();
}
- List<ResolveInfo> available =
- mContext.getPackageManager().queryIntentServicesAsUser(
- new Intent(RecognitionService.SERVICE_INTERFACE),
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
- int numAvailable = available.size();
- if (numAvailable == 0) {
+ List<RecognitionServiceInfo> available =
+ RecognitionServiceInfo.getAvailableServices(mContext, userHandle);
+ if (available.size() == 0) {
Slog.w(TAG, "no available voice recognition services found for user " + userHandle);
return null;
} else {
+ List<RecognitionServiceInfo> nonSelectableAsDefault =
+ removeNonSelectableAsDefault(available);
+ if (available.size() == 0) {
+ Slog.w(TAG, "No selectableAsDefault recognition services found for user "
+ + userHandle + ". Falling back to non selectableAsDefault ones.");
+ available = nonSelectableAsDefault;
+ }
+ int numAvailable = available.size();
if (prefPackage != null) {
- for (int i=0; i<numAvailable; i++) {
- ServiceInfo serviceInfo = available.get(i).serviceInfo;
+ for (int i = 0; i < numAvailable; i++) {
+ ServiceInfo serviceInfo = available.get(i).getServiceInfo();
if (prefPackage.equals(serviceInfo.packageName)) {
return new ComponentName(serviceInfo.packageName, serviceInfo.name);
}
@@ -672,11 +700,22 @@
Slog.w(TAG, "more than one voice recognition service found, picking first");
}
- ServiceInfo serviceInfo = available.get(0).serviceInfo;
+ ServiceInfo serviceInfo = available.get(0).getServiceInfo();
return new ComponentName(serviceInfo.packageName, serviceInfo.name);
}
}
+ private List<RecognitionServiceInfo> removeNonSelectableAsDefault(
+ List<RecognitionServiceInfo> services) {
+ List<RecognitionServiceInfo> nonSelectableAsDefault = new ArrayList<>();
+ for (int i = services.size() - 1; i >= 0; i--) {
+ if (!services.get(i).isSelectableAsDefault()) {
+ nonSelectableAsDefault.add(services.remove(i));
+ }
+ }
+ return nonSelectableAsDefault;
+ }
+
@Nullable
public String getDefaultRecognizer() {
String recognizer = mContext.getString(R.string.config_systemSpeechRecognizer);
@@ -983,22 +1022,22 @@
}
}
+ //----------------- Hotword Detection/Validation APIs --------------------------------//
+
@Override
- public void setHotwordDetectionServiceConfig(@Nullable PersistableBundle options,
- @Nullable SharedMemory sharedMemory) {
+ public void updateState(@Nullable PersistableBundle options,
+ @Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
enforceCallingPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION);
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
if (mImpl == null) {
- Slog.w(TAG,
- "setHotwordDetectionServiceConfig without running voice"
- + " interaction service");
+ Slog.w(TAG, "updateState without running voice interaction service");
return;
}
final long caller = Binder.clearCallingIdentity();
try {
- mImpl.setHotwordDetectionServiceConfigLocked(options, sharedMemory);
+ mImpl.updateStateLocked(options, sharedMemory, callback);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -1025,6 +1064,72 @@
}
}
+ @Override
+ public void startListeningFromMic(
+ AudioFormat audioFormat,
+ IMicrophoneHotwordDetectionVoiceInteractionCallback callback)
+ throws RemoteException {
+ enforceCallingPermission(Manifest.permission.RECORD_AUDIO);
+ enforceCallingPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD);
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService();
+
+ if (mImpl == null) {
+ Slog.w(TAG, "startListeningFromMic without running voice interaction service");
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.startListeningFromMicLocked(audioFormat, callback);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
+ @Override
+ public void startListeningFromExternalSource(
+ ParcelFileDescriptor audioStream,
+ AudioFormat audioFormat,
+ PersistableBundle options,
+ IMicrophoneHotwordDetectionVoiceInteractionCallback callback)
+ throws RemoteException {
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService();
+
+ if (mImpl == null) {
+ Slog.w(TAG, "startListeningFromExternalSource without running voice"
+ + " interaction service");
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.startListeningFromExternalSourceLocked(
+ audioStream, audioFormat, options, callback);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
+ @Override
+ public void stopListeningFromMic() throws RemoteException {
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService();
+
+ if (mImpl == null) {
+ Slog.w(TAG, "stopListeningFromMic without running voice interaction service");
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.stopListeningFromMicLocked();
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
//----------------- Model management APIs --------------------------------//
@Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 0d4c302..6922ccc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -40,15 +40,18 @@
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.media.AudioFormat;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SharedMemory;
import android.os.UserHandle;
+import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionService;
@@ -401,10 +404,10 @@
return mInfo.getSupportsLocalInteraction();
}
- public void setHotwordDetectionServiceConfigLocked(@Nullable PersistableBundle options,
- @Nullable SharedMemory sharedMemory) {
+ public void updateStateLocked(@Nullable PersistableBundle options,
+ @Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
if (DEBUG) {
- Slog.d(TAG, "setHotwordDetectionServiceConfigLocked");
+ Slog.d(TAG, "updateStateLocked");
}
if (mHotwordDetectionComponentName == null) {
Slog.w(TAG, "Hotword detection service name not found");
@@ -424,9 +427,9 @@
if (mHotwordDetectionConnection == null) {
mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false,
- options, sharedMemory);
+ options, sharedMemory, callback);
} else {
- mHotwordDetectionConnection.setConfigLocked(options, sharedMemory);
+ mHotwordDetectionConnection.updateStateLocked(options, sharedMemory);
}
}
@@ -444,6 +447,52 @@
mHotwordDetectionConnection = null;
}
+ public void startListeningFromMicLocked(
+ AudioFormat audioFormat,
+ IMicrophoneHotwordDetectionVoiceInteractionCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningFromMic");
+ }
+
+ if (mHotwordDetectionConnection == null) {
+ // TODO: callback.onError();
+ return;
+ }
+
+ mHotwordDetectionConnection.startListeningFromMic(audioFormat, callback);
+ }
+
+ public void startListeningFromExternalSourceLocked(
+ ParcelFileDescriptor audioStream,
+ AudioFormat audioFormat,
+ @Nullable PersistableBundle options,
+ IMicrophoneHotwordDetectionVoiceInteractionCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningFromExternalSource");
+ }
+
+ if (mHotwordDetectionConnection == null) {
+ // TODO: callback.onError();
+ return;
+ }
+
+ mHotwordDetectionConnection
+ .startListeningFromExternalSource(audioStream, audioFormat, options, callback);
+ }
+
+ public void stopListeningFromMicLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "stopListeningFromMic");
+ }
+
+ if (mHotwordDetectionConnection == null) {
+ Slog.w(TAG, "stopListeningFromMic() called but connection isn't established");
+ return;
+ }
+
+ mHotwordDetectionConnection.stopListening();
+ }
+
public IRecognitionStatusCallback createSoundTriggerCallbackLocked(
IHotwordRecognitionStatusCallback callback) {
if (DEBUG) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 4e64838..d77ab2b 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -687,7 +687,11 @@
public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000;
/**
- * Connection is using Cross SIM Calling.
+ * Connection is using cross sim technology.
+ * <p>
+ * Indicates that the {@link Connection} is using a cross sim technology which would
+ * register IMS over internet APN of default data subscription.
+ * <p>
*/
public static final int PROPERTY_CROSS_SIM = 0x00004000;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1677c8c..4886789 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -25,6 +25,8 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -1004,6 +1006,17 @@
PRESENTATION_PAYPHONE})
public @interface Presentation {}
+
+ /**
+ * Enable READ_PHONE_STATE protection on APIs querying and notifying call state, such as
+ * {@code TelecomManager#getCallState}, {@link TelephonyManager#getCallStateForSubscription()},
+ * and {@link android.telephony.TelephonyCallback.CallStateListener}.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ // this magic number is a bug ID
+ public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L;
+
private static final String TAG = "TelecomManager";
@@ -1758,21 +1771,23 @@
* {@link TelephonyManager#CALL_STATE_OFFHOOK}
* {@link TelephonyManager#CALL_STATE_IDLE}
*
- * Note that this API does not require the
- * {@link android.Manifest.permission#READ_PHONE_STATE} permission. This is intentional, to
- * preserve the behavior of {@link TelephonyManager#getCallState()}, which also did not require
- * the permission.
- *
* Takes into consideration both managed and self-managed calls.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
+ * targeting API level 31+.
*
* @hide
*/
+ @RequiresPermission(anyOf = {READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE}, conditional = true)
@SystemApi
public @CallState int getCallState() {
ITelecomService service = getTelecomService();
if (service != null) {
try {
- return service.getCallState();
+ return service.getCallStateUsingPackage(mContext.getPackageName(),
+ mContext.getAttributionTag());
} catch (RemoteException e) {
Log.d(TAG, "RemoteException calling getCallState().", e);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 78283fa..18afde7 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -195,11 +195,18 @@
/**
* @see TelecomServiceImpl#getCallState
+ * Note: only kept around to not break app compat, however this will throw a SecurityException
+ * on API 31+.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
int getCallState();
/**
+ * @see TelecomServiceImpl#getCallState
+ */
+ int getCallStateUsingPackage(String callingPackage, String callingFeatureId);
+
+ /**
* @see TelecomServiceImpl#endCall
*/
boolean endCall(String callingPackage);
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index a012498..3b28616 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -293,6 +293,14 @@
}
/**
+ * Return a copy of this PhysicalChannelConfig object but redact all the location info.
+ * @hide
+ */
+ public PhysicalChannelConfig createLocationInfoSanitizedCopy() {
+ return new Builder(this).setPhysicalCellId(PHYSICAL_CELL_ID_UNKNOWN).build();
+ }
+
+ /**
* @return String representation of the connection status
* @hide
*/
@@ -541,6 +549,23 @@
mBand = BAND_UNKNOWN;
}
+ /**
+ * Builder object constructed from existing PhysicalChannelConfig object.
+ * @hide
+ */
+ public Builder(PhysicalChannelConfig config) {
+ mNetworkType = config.getNetworkType();
+ mFrequencyRange = config.getFrequencyRange();
+ mDownlinkChannelNumber = config.getDownlinkChannelNumber();
+ mUplinkChannelNumber = config.getUplinkChannelNumber();
+ mCellBandwidthDownlinkKhz = config.getCellBandwidthDownlinkKhz();
+ mCellBandwidthUplinkKhz = config.getCellBandwidthUplinkKhz();
+ mCellConnectionStatus = config.getConnectionStatus();
+ mContextIds = Arrays.copyOf(config.getContextIds(), config.getContextIds().length);
+ mPhysicalCellId = config.getPhysicalCellId();
+ mBand = config.getBand();
+ }
+
public PhysicalChannelConfig build() {
return new PhysicalChannelConfig(this);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d2da51a..653aa9c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -105,6 +105,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
import com.android.internal.telephony.ICallForwardingInfoCallback;
@@ -139,6 +140,7 @@
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -431,6 +433,27 @@
}
/**
+ * Post a runnable to the BackgroundThread.
+ *
+ * Used to invoke user callbacks without calling into the caller's executor from the caller's
+ * calling thread context, for example to provide asynchronous error information that is
+ * generated locally (not over a binder thread).
+ *
+ * <p>This is not necessary unless you are invoking caller's code asynchronously from within
+ * the caller's thread context.
+ *
+ * @param r a runnable.
+ */
+ private static void runOnBackgroundThread(@NonNull Runnable r) {
+ try {
+ BackgroundThread.getExecutor().execute(r);
+ } catch (RejectedExecutionException e) {
+ throw new IllegalStateException(
+ "Failed to post a callback from the caller's thread context.", e);
+ }
+ }
+
+ /**
* Returns the multi SIM variant
* Returns DSDS for Dual SIM Dual Standby
* Returns DSDA for Dual SIM Dual Active
@@ -5702,9 +5725,20 @@
* Note: The call state returned via this method may differ from what is reported by
* {@link PhoneStateListener#onCallStateChanged(int, String)}, as that callback only considers
* Telephony (mobile) calls.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
+ * targeting API level 31+.
*
* @return the current call state.
+ * @deprecated Use {@link #getCallStateForSubscription} to retrieve the call state for a
+ * specific telephony subscription (which allows carrier privileged apps),
+ * {@link TelephonyCallback.CallStateListener} for real-time call state updates, or
+ * {@link TelecomManager#isInCall()}, which supplies an aggregate "in call" state for the entire
+ * device.
*/
+ @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
+ @Deprecated
public @CallState int getCallState() {
if (mContext != null) {
TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
@@ -5716,19 +5750,48 @@
}
/**
+ * Retrieve the call state for a specific subscription that was specified when this
+ * TelephonyManager instance was created.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or that the calling
+ * application has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * @see TelephonyManager#createForSubscriptionId(int)
+ * @see TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle)
+ * @return The call state of the subscription associated with this TelephonyManager instance.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public @CallState int getCallStateForSubscription() {
+ return getCallState(getSubId());
+ }
+
+ /**
* Returns the Telephony call state for calls on a specific subscription.
* <p>
* Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()}
* considers the state of calls from other {@link android.telecom.ConnectionService}
* implementations.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
+ * targeting API level 31+ or that the calling application has carrier privileges
+ * (see {@link #hasCarrierPrivileges()}).
*
* @param subId the subscription to check call state for.
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
public @CallState int getCallState(int subId) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
- return getCallStateForSlot(phoneId);
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ return CALL_STATE_IDLE;
+ }
+ try {
+ return telephony.getCallStateForSubscription(subId, mContext.getPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ return CALL_STATE_IDLE;
+ }
}
/**
@@ -5745,22 +5808,28 @@
* Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()}
* considers the state of calls from other {@link android.telecom.ConnectionService}
* implementations.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
+ * targeting API level 31+ or that the calling application has carrier privileges
+ * (see {@link #hasCarrierPrivileges()}).
*
* @param slotIndex the SIM slot index to check call state for.
* @hide
*/
+ @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
public @CallState int getCallStateForSlot(int slotIndex) {
try {
+ int[] subId = SubscriptionManager.getSubId(slotIndex);
ITelephony telephony = getITelephony();
- if (telephony == null)
+ if (telephony == null || subId == null || subId.length == 0) {
return CALL_STATE_IDLE;
- return telephony.getCallStateForSlot(slotIndex);
- } catch (RemoteException ex) {
+ }
+ return telephony.getCallStateForSubscription(subId[0], mContext.getPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException | NullPointerException ex) {
// the phone process is restarting.
return CALL_STATE_IDLE;
- } catch (NullPointerException ex) {
- // the phone process is restarting.
- return CALL_STATE_IDLE;
}
}
@@ -6240,7 +6309,7 @@
/**
* Error response to
- * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
+ * {@link TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
*
* Invoked when an error condition prevents updated {@link CellInfo} from being fetched
* and returned from the modem. Callers of requestCellInfoUpdate() should override this
@@ -6258,6 +6327,20 @@
};
/**
+ * Used for checking if the target SDK version for the current process is S or above.
+ *
+ * <p> Applies to the following methods:
+ * {@link #requestCellInfoUpdate},
+ * {@link #setPreferredOpportunisticDataSubscription},
+ * {@link #updateAvailableNetworks},
+ * requestNumberVerification(),
+ * setSimPowerStateForSlot(),
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long NULL_TELEPHONY_THROW_NO_CB = 182185642L;
+
+ /**
* Requests all available cell information from the current subscription for observed
* camped/registered, serving, and neighboring cells.
*
@@ -6277,7 +6360,14 @@
@NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
try {
ITelephony telephony = getITelephony();
- if (telephony == null) return;
+ if (telephony == null) {
+ if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+ throw new IllegalStateException("Telephony is null");
+ } else {
+ return;
+ }
+ }
+
telephony.requestCellInfoUpdate(
getSubId(),
new ICellInfoCallback.Stub() {
@@ -6304,6 +6394,8 @@
}
}, getOpPackageName(), getAttributionTag());
} catch (RemoteException ex) {
+ runOnBackgroundThread(() -> executor.execute(
+ () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex)));
}
}
@@ -6331,7 +6423,14 @@
@NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
try {
ITelephony telephony = getITelephony();
- if (telephony == null) return;
+ if (telephony == null) {
+ if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+ throw new IllegalStateException("Telephony is null");
+ } else {
+ return;
+ }
+ }
+
telephony.requestCellInfoUpdateWithWorkSource(
getSubId(),
new ICellInfoCallback.Stub() {
@@ -6359,6 +6458,8 @@
}
}, getOpPackageName(), getAttributionTag(), workSource);
} catch (RemoteException ex) {
+ runOnBackgroundThread(() -> executor.execute(
+ () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex)));
}
}
@@ -7325,14 +7426,21 @@
try {
ITelephony telephony = getITelephony();
- if (telephony != null) {
- telephony.requestNumberVerification(range, timeoutMillis, internalCallback,
- getOpPackageName());
+ if (telephony == null) {
+ if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+ throw new IllegalStateException("Telephony is null");
+ } else {
+ return;
+ }
}
+
+ telephony.requestNumberVerification(range, timeoutMillis, internalCallback,
+ getOpPackageName());
} catch (RemoteException ex) {
Rlog.e(TAG, "requestNumberVerification RemoteException", ex);
- executor.execute(() ->
- callback.onVerificationFailed(NumberVerificationCallback.REASON_UNSPECIFIED));
+ runOnBackgroundThread(() -> executor.execute(
+ () -> callback.onVerificationFailed(
+ NumberVerificationCallback.REASON_UNSPECIFIED)));
}
}
@@ -10701,6 +10809,8 @@
}
try {
ITelephony telephony = getITelephony();
+ if (telephony == null) throw new IllegalStateException("Telephony is null.");
+
IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
@Override
public void accept(int result) {
@@ -10708,11 +10818,18 @@
Binder.withCleanCallingIdentity(() -> callback.accept(result)));
}
};
- if (telephony != null) {
- telephony.setSimPowerStateForSlotWithCallback(slotIndex, state, internalCallback);
+ if (telephony == null) {
+ if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+ throw new IllegalStateException("Telephony is null");
+ } else {
+ return;
+ }
}
+ telephony.setSimPowerStateForSlotWithCallback(slotIndex, state, internalCallback);
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e);
+ runOnBackgroundThread(() -> executor.execute(
+ () -> callback.accept(SET_SIM_POWER_STATE_MODEM_ERROR)));
} catch (SecurityException e) {
Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot",
e);
@@ -13285,22 +13402,12 @@
try {
IOns iOpportunisticNetworkService = getIOns();
if (iOpportunisticNetworkService == null) {
- if (executor == null || callback == null) {
- return;
+ if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+ throw new IllegalStateException("Opportunistic Network Service is null");
+ } else {
+ // Let the general remote exception handling catch this.
+ throw new RemoteException("Null Opportunistic Network Service!");
}
- final long identity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> {
- if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
- callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
- } else {
- callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
- }
- });
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return;
}
ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() {
@Override
@@ -13323,9 +13430,18 @@
.setPreferredDataSubscriptionId(subId, needValidation, callbackStub,
pkgForDebug);
} catch (RemoteException ex) {
- Rlog.e(TAG, "setPreferredDataSubscriptionId RemoteException", ex);
+ Rlog.e(TAG, "setPreferredOpportunisticDataSubscription RemoteException", ex);
+ if (executor == null || callback == null) {
+ return;
+ }
+ runOnBackgroundThread(() -> executor.execute(() -> {
+ if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+ callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
+ } else {
+ callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
+ }
+ }));
}
- return;
}
/**
@@ -13382,37 +13498,18 @@
@Nullable @CallbackExecutor Executor executor,
@UpdateAvailableNetworksResult @Nullable Consumer<Integer> callback) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ Objects.requireNonNull(availableNetworks, "availableNetworks must not be null.");
try {
IOns iOpportunisticNetworkService = getIOns();
- if (iOpportunisticNetworkService == null || availableNetworks == null) {
- if (executor == null || callback == null) {
- return;
- }
- if (iOpportunisticNetworkService == null) {
- final long identity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> {
- if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
- callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION);
- } else {
- callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE);
- }
- });
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ if (iOpportunisticNetworkService == null) {
+ if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+ throw new IllegalStateException("Opportunistic Network Service is null");
} else {
- final long identity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> {
- callback.accept(UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
- });
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ // Let the general remote exception handling catch this.
+ throw new RemoteException("Null Opportunistic Network Service!");
}
- return;
}
+
IUpdateAvailableNetworksCallback callbackStub =
new IUpdateAvailableNetworksCallback.Stub() {
@Override
@@ -13420,20 +13517,25 @@
if (executor == null || callback == null) {
return;
}
- final long identity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> {
- callback.accept(result);
- });
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ Binder.withCleanCallingIdentity(() -> {
+ executor.execute(() -> callback.accept(result));
+ });
}
};
- iOpportunisticNetworkService.updateAvailableNetworks(availableNetworks, callbackStub,
- pkgForDebug);
+ iOpportunisticNetworkService
+ .updateAvailableNetworks(availableNetworks, callbackStub, pkgForDebug);
} catch (RemoteException ex) {
Rlog.e(TAG, "updateAvailableNetworks RemoteException", ex);
+ if (executor == null || callback == null) {
+ return;
+ }
+ runOnBackgroundThread(() -> executor.execute(() -> {
+ if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+ callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION);
+ } else {
+ callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE);
+ }
+ }));
}
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index b503733..08f5613 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -114,11 +114,15 @@
public static final int TYPE_MCX = ApnTypes.MCX;
/** APN type for XCAP. */
public static final int TYPE_XCAP = ApnTypes.XCAP;
+ /** APN type for VSIM. */
+ public static final int TYPE_VSIM = 1 << 12; // TODO: Refer to ApnTypes.VSIM
+ /** APN type for BIP. */
+ public static final int TYPE_BIP = 1 << 13; // TODO: Refer to ApnTypes.BIP
/**
* APN type for ENTERPRISE.
* @hide
*/
- public static final int TYPE_ENTERPRISE = TYPE_XCAP << 1;
+ public static final int TYPE_ENTERPRISE = TYPE_BIP << 1;
/** @hide */
@IntDef(flag = true, prefix = {"TYPE_"}, value = {
@@ -134,6 +138,8 @@
TYPE_EMERGENCY,
TYPE_MCX,
TYPE_XCAP,
+ TYPE_BIP,
+ TYPE_VSIM,
TYPE_ENTERPRISE,
})
@Retention(RetentionPolicy.SOURCE)
@@ -174,6 +180,8 @@
TYPE_MMS_STRING,
TYPE_SUPL_STRING,
TYPE_XCAP_STRING,
+ TYPE_VSIM_STRING,
+ TYPE_BIP_STRING,
TYPE_ENTERPRISE_STRING,
}, prefix = "TYPE_", suffix = "_STRING")
@Retention(RetentionPolicy.SOURCE)
@@ -315,8 +323,33 @@
@SystemApi
public static final String TYPE_XCAP_STRING = "xcap";
+
+
+ /**
+ * APN type for Virtual SIM service.
+ *
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
+ * @hide
+ */
+ @SystemApi
+ public static final String TYPE_VSIM_STRING = "vsim";
+
+ /**
+ * APN type for Bearer Independent Protocol.
+ *
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
+ * @hide
+ */
+ @SystemApi
+ public static final String TYPE_BIP_STRING = "bip";
+
/**
* APN type for ENTERPRISE traffic.
+ *
+ * Note: String representations of APN types are intended for system apps to communicate with
+ * modem components or carriers. Non-system apps should use the integer variants instead.
* @hide
*/
public static final String TYPE_ENTERPRISE_STRING = "enterprise";
@@ -401,6 +434,8 @@
APN_TYPE_STRING_MAP.put(TYPE_MCX_STRING, TYPE_MCX);
APN_TYPE_STRING_MAP.put(TYPE_XCAP_STRING, TYPE_XCAP);
APN_TYPE_STRING_MAP.put(TYPE_ENTERPRISE_STRING, TYPE_ENTERPRISE);
+ APN_TYPE_STRING_MAP.put(TYPE_VSIM_STRING, TYPE_VSIM);
+ APN_TYPE_STRING_MAP.put(TYPE_BIP_STRING, TYPE_BIP);
APN_TYPE_INT_MAP = new ArrayMap<>();
APN_TYPE_INT_MAP.put(TYPE_DEFAULT, TYPE_DEFAULT_STRING);
@@ -416,6 +451,8 @@
APN_TYPE_INT_MAP.put(TYPE_MCX, TYPE_MCX_STRING);
APN_TYPE_INT_MAP.put(TYPE_XCAP, TYPE_XCAP_STRING);
APN_TYPE_INT_MAP.put(TYPE_ENTERPRISE, TYPE_ENTERPRISE_STRING);
+ APN_TYPE_INT_MAP.put(TYPE_VSIM, TYPE_VSIM_STRING);
+ APN_TYPE_INT_MAP.put(TYPE_BIP, TYPE_BIP_STRING);
PROTOCOL_STRING_MAP = new ArrayMap<>();
PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
@@ -2194,7 +2231,7 @@
public ApnSetting build() {
if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI
| TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX
- | TYPE_XCAP | TYPE_ENTERPRISE)) == 0
+ | TYPE_XCAP | TYPE_VSIM | TYPE_BIP | TYPE_ENTERPRISE)) == 0
|| TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) {
return null;
}
diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
index 406c38b..9bc7a5c 100644
--- a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
+++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
@@ -53,7 +53,7 @@
*
* @return the qci of the session
*/
- public int getQci() {
+ public int getQosIdentifier() {
return mQci;
}
@@ -66,7 +66,7 @@
*
* @return the guaranteed bit rate in kbps
*/
- public long getGuaranteedUplinkBitRate() {
+ public long getGuaranteedUplinkBitRateKbps() {
return mGuaranteedUplinkBitRate;
}
@@ -79,7 +79,7 @@
*
* @return the guaranteed bit rate in kbps
*/
- public long getGuaranteedDownlinkBitRate() {
+ public long getGuaranteedDownlinkBitRateKbps() {
return mGuaranteedDownlinkBitRate;
}
@@ -92,7 +92,7 @@
*
* @return the max uplink bit rate in kbps
*/
- public long getMaxUplinkBitRate() {
+ public long getMaxUplinkBitRateKbps() {
return mMaxUplinkBitRate;
}
@@ -105,7 +105,7 @@
*
* @return the max downlink bit rate in kbps
*/
- public long getMaxDownlinkBitRate() {
+ public long getMaxDownlinkBitRateKbps() {
return mMaxDownlinkBitRate;
}
diff --git a/telephony/java/android/telephony/data/NrQosSessionAttributes.java b/telephony/java/android/telephony/data/NrQosSessionAttributes.java
index 857ccb9..4c37687 100644
--- a/telephony/java/android/telephony/data/NrQosSessionAttributes.java
+++ b/telephony/java/android/telephony/data/NrQosSessionAttributes.java
@@ -16,6 +16,7 @@
package android.telephony.data;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.net.QosSessionAttributes;
@@ -26,6 +27,7 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -40,7 +42,7 @@
public final class NrQosSessionAttributes implements Parcelable, QosSessionAttributes {
private static final String TAG = NrQosSessionAttributes.class.getSimpleName();
private final int m5Qi;
- private final int mQfi;
+ private final @IntRange(from=1, to=63) int mQfi;
private final long mMaxUplinkBitRate;
private final long mMaxDownlinkBitRate;
private final long mGuaranteedUplinkBitRate;
@@ -55,7 +57,7 @@
*
* @return the 5QI of the QOS flow
*/
- public int get5Qi() {
+ public int getQosIdentifier() {
return m5Qi;
}
@@ -65,7 +67,7 @@
*
* @return the QOS flow identifier of the session
*/
- public int getQfi() {
+ public @IntRange(from=1, to=63) int getQosFlowIdentifier() {
return mQfi;
}
@@ -78,7 +80,7 @@
*
* @return the guaranteed bit rate in kbps
*/
- public long getGuaranteedUplinkBitRate() {
+ public long getGuaranteedUplinkBitRateKbps() {
return mGuaranteedUplinkBitRate;
}
@@ -91,7 +93,7 @@
*
* @return the guaranteed bit rate in kbps
*/
- public long getGuaranteedDownlinkBitRate() {
+ public long getGuaranteedDownlinkBitRateKbps() {
return mGuaranteedDownlinkBitRate;
}
@@ -104,7 +106,7 @@
*
* @return the max uplink bit rate in kbps
*/
- public long getMaxUplinkBitRate() {
+ public long getMaxUplinkBitRateKbps() {
return mMaxUplinkBitRate;
}
@@ -117,7 +119,7 @@
*
* @return the max downlink bit rate in kbps
*/
- public long getMaxDownlinkBitRate() {
+ public long getMaxDownlinkBitRateKbps() {
return mMaxDownlinkBitRate;
}
@@ -129,8 +131,9 @@
*
* @return the averaging window duration in milliseconds
*/
- public long getAveragingWindow() {
- return mAveragingWindow;
+ @NonNull
+ public Duration getBitRateWindowDuration() {
+ return Duration.ofMillis(mAveragingWindow);
}
/**
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 6315b24..b384e50 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -391,6 +391,7 @@
* event to the framework.
* @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
* exchange if it is supported by the device.
+ * @hide
*/
public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
@NonNull Executor executor, @NonNull CapabilityExchangeEventListener listener) {
@@ -399,14 +400,45 @@
}
/**
+ * Retrieve the implementation of UCE for this {@link RcsFeature}, which can use either
+ * presence or OPTIONS for capability exchange.
+ *
+ * Will only be requested by the framework if capability exchange is configured
+ * as capable during a
+ * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+ * operation and the RcsFeature sets the status of the capability to true using
+ * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+ *
+ * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+ * event to the framework.
+ * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
+ * exchange if it is supported by the device.
+ */
+ public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
+ @NonNull CapabilityExchangeEventListener listener) {
+ // Base Implementation, override to implement functionality
+ return new RcsCapabilityExchangeImplBase();
+ }
+
+ /**
* Remove the given CapabilityExchangeImplBase instance.
* @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be removed.
+ * @hide
*/
public void removeCapabilityExchangeImpl(
@NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
// Override to implement the process of removing RcsCapabilityExchangeImplBase instance.
}
+ /**
+ * Remove the given CapabilityExchangeImplBase instance.
+ * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed.
+ */
+ public void destroyCapabilityExchangeImpl(
+ @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
+ // Override to implement the process of destroying RcsCapabilityExchangeImplBase instance.
+ }
+
/**{@inheritDoc}*/
@Override
public void onFeatureRemoved() {
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 25b9446..a117adc 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -356,12 +356,13 @@
void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
}
- private final Executor mBinderExecutor;
+ private Executor mBinderExecutor;
/**
* Create a new RcsCapabilityExchangeImplBase instance.
*
* @param executor The executor that remote calls from the framework will be called on.
+ * @hide
*/
public RcsCapabilityExchangeImplBase(@NonNull Executor executor) {
if (executor == null) {
@@ -371,6 +372,12 @@
}
/**
+ * Create a new RcsCapabilityExchangeImplBase instance.
+ */
+ public RcsCapabilityExchangeImplBase() {
+ }
+
+ /**
* The user capabilities of one or multiple contacts have been requested by the framework.
* <p>
* The implementer must follow up this call with an
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 46752b7..afc538d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -298,9 +298,9 @@
int getCallState();
/**
- * Returns the call state for a slot.
+ * Returns the call state for a specific subscriiption.
*/
- int getCallStateForSlot(int slotIndex);
+ int getCallStateForSubscription(int subId, String callingPackage, String featureId);
/**
* Replaced by getDataActivityForSubId.
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index 5b9f67e..7be42f4 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.AttributionSource;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
@@ -60,21 +61,20 @@
*/
private class InversionIContentProvider implements IContentProvider {
@Override
- public ContentProviderResult[] applyBatch(String callingPackage,
- @Nullable String featureId, String authority,
- ArrayList<ContentProviderOperation> operations)
+ public ContentProviderResult[] applyBatch(@NonNull AttributionSource attributionSource,
+ String authority, ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
return MockContentProvider.this.applyBatch(authority, operations);
}
@Override
- public int bulkInsert(String callingPackage, @Nullable String featureId, Uri url,
+ public int bulkInsert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues[] initialValues) throws RemoteException {
return MockContentProvider.this.bulkInsert(url, initialValues);
}
@Override
- public int delete(String callingPackage, @Nullable String featureId, Uri url,
+ public int delete(@NonNull AttributionSource attributionSource, Uri url,
Bundle extras) throws RemoteException {
return MockContentProvider.this.delete(url, extras);
}
@@ -90,40 +90,40 @@
}
@Override
- public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
+ public Uri insert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues initialValues, Bundle extras) throws RemoteException {
return MockContentProvider.this.insert(url, initialValues, extras);
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPackage,
- @Nullable String featureId, Uri url, String mode, ICancellationSignal signal)
+ public AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openAssetFile(url, mode);
}
@Override
- public ParcelFileDescriptor openFile(String callingPackage, @Nullable String featureId,
- Uri url, String mode, ICancellationSignal signal, IBinder callerToken)
+ public ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openFile(url, mode);
}
@Override
- public Cursor query(String callingPackage, @Nullable String featureId, Uri url,
+ public Cursor query(@NonNull AttributionSource attributionSource, Uri url,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.query(url, projection, queryArgs, null);
}
@Override
- public int update(String callingPackage, @Nullable String featureId, Uri url,
+ public int update(@NonNull AttributionSource attributionSource, Uri url,
ContentValues values, Bundle extras) throws RemoteException {
return MockContentProvider.this.update(url, values, extras);
}
@Override
- public Bundle call(String callingPackage, @Nullable String featureId, String authority,
+ public Bundle call(@NonNull AttributionSource attributionSource, String authority,
String method, String request, Bundle args) throws RemoteException {
return MockContentProvider.this.call(authority, method, request, args);
}
@@ -139,9 +139,10 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPackage,
- @Nullable String featureId, Uri url, String mimeType, Bundle opts,
- ICancellationSignal signal) throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(
+ @NonNull AttributionSource attributionSource, Uri url, String mimeType,
+ Bundle opts, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
}
@@ -151,37 +152,37 @@
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ public Uri canonicalize(@NonNull AttributionSource attributionSource, Uri uri)
throws RemoteException {
return MockContentProvider.this.canonicalize(uri);
}
@Override
- public void canonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ public void canonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) {
MockContentProvider.this.canonicalizeAsync(uri, callback);
}
@Override
- public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ public Uri uncanonicalize(@NonNull AttributionSource attributionSource, Uri uri)
throws RemoteException {
return MockContentProvider.this.uncanonicalize(uri);
}
@Override
- public void uncanonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ public void uncanonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback callback) {
MockContentProvider.this.uncanonicalizeAsync(uri, callback);
}
@Override
- public boolean refresh(String callingPkg, @Nullable String featureId, Uri url,
+ public boolean refresh(@NonNull AttributionSource attributionSource, Uri url,
Bundle args, ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.refresh(url, args);
}
@Override
- public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri,
+ public int checkUriPermission(@NonNull AttributionSource attributionSource, Uri uri,
int uid, int modeFlags) {
return MockContentProvider.this.checkUriPermission(uri, uid, modeFlags);
}
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index 82a1cf7..b81c707 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -16,7 +16,9 @@
package android.test.mock;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.AttributionSource;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
@@ -46,14 +48,14 @@
*/
public class MockIContentProvider implements IContentProvider {
@Override
- public int bulkInsert(String callingPackage, @Nullable String featureId, Uri url,
+ public int bulkInsert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues[] initialValues) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
@SuppressWarnings("unused")
- public int delete(String callingPackage, @Nullable String featureId, Uri url,
+ public int delete(@NonNull AttributionSource attributionSource, Uri url,
Bundle extras) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -75,31 +77,31 @@
@Override
@SuppressWarnings("unused")
- public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
+ public Uri insert(@NonNull AttributionSource attributionSource, Uri url,
ContentValues initialValues, Bundle extras) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public ParcelFileDescriptor openFile(String callingPackage, @Nullable String featureId,
- Uri url, String mode, ICancellationSignal signal, IBinder callerToken) {
+ public ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mode, ICancellationSignal signal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPackage, @Nullable String featureId,
+ public AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionSource,
Uri uri, String mode, ICancellationSignal signal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public ContentProviderResult[] applyBatch(String callingPackage, @Nullable String featureId,
+ public ContentProviderResult[] applyBatch(@NonNull AttributionSource attributionSource,
String authority, ArrayList<ContentProviderOperation> operations) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public Cursor query(String callingPackage, @Nullable String featureId, Uri url,
+ public Cursor query(@NonNull AttributionSource attributionSource, Uri url,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable ICancellationSignal cancellationSignal) {
throw new UnsupportedOperationException("unimplemented mock method");
@@ -111,13 +113,13 @@
}
@Override
- public int update(String callingPackage, @Nullable String featureId, Uri url,
+ public int update(@NonNull AttributionSource attributionSource, Uri url,
ContentValues values, Bundle extras) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public Bundle call(String callingPackage, @Nullable String featureId, String authority,
+ public Bundle call(@NonNull AttributionSource attributionSource, String authority,
String method, String request, Bundle args) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -133,9 +135,9 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPackage,
- @Nullable String featureId, Uri url, String mimeType, Bundle opts,
- ICancellationSignal signal) throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(@NonNull AttributionSource attributionSource,
+ Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -145,48 +147,48 @@
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
+ public Uri canonicalize(@NonNull AttributionSource attributionSource, Uri uri) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
@SuppressWarnings("deprecation")
- public void canonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ public void canonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback remoteCallback) {
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
final Bundle bundle = new Bundle();
bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- canonicalize(callingPkg, featureId, uri));
+ canonicalize(attributionSource, uri));
remoteCallback.sendResult(bundle);
});
}
@Override
- public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
+ public Uri uncanonicalize(@NonNull AttributionSource attributionSource, Uri uri) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
@SuppressWarnings("deprecation")
- public void uncanonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ public void uncanonicalizeAsync(@NonNull AttributionSource attributionSource, Uri uri,
RemoteCallback remoteCallback) {
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
final Bundle bundle = new Bundle();
bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
- uncanonicalize(callingPkg, featureId, uri));
+ uncanonicalize(attributionSource, uri));
remoteCallback.sendResult(bundle);
});
}
@Override
- public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args,
+ public boolean refresh(@NonNull AttributionSource attributionSource, Uri url, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
/** {@hide} */
@Override
- public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid,
+ public int checkUriPermission(@NonNull AttributionSource attributionSource, Uri uri, int uid,
int modeFlags) {
throw new UnsupportedOperationException("unimplemented mock method call");
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index e6a4501..ed0a98d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -101,10 +101,10 @@
}
} else {
assertLayersStart {
- this.coversAtLeast(startingBounds)
+ this.visibleRegion().coversAtLeast(startingBounds)
}
assertLayersEnd {
- this.coversAtLeast(endingBounds)
+ this.visibleRegion().coversAtLeast(endingBounds)
}
}
}
@@ -152,10 +152,10 @@
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
assertLayersStart {
- this.coversExactly(startingPos, NAV_BAR_LAYER_NAME)
+ this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(startingPos)
}
assertLayersEnd {
- this.coversExactly(endingPos, NAV_BAR_LAYER_NAME)
+ this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(endingPos)
}
}
@@ -168,10 +168,10 @@
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
assertLayersStart {
- this.coversExactly(startingPos, STATUS_BAR_WINDOW_NAME)
+ this.visibleRegion(STATUS_BAR_WINDOW_NAME).coversExactly(startingPos)
}
assertLayersEnd {
- this.coversExactly(endingPos, STATUS_BAR_WINDOW_NAME)
+ this.visibleRegion(STATUS_BAR_WINDOW_NAME).coversExactly(endingPos)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index e118363..a524466 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
@@ -87,13 +88,13 @@
testSpec.statusBarLayerIsAlwaysVisible()
}
- @Presubmit
+ @FlakyTest
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
}
- @Presubmit
+ @FlakyTest
@Test
open fun statusBarLayerRotatesScales() {
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 619a05e..bd7c185 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -34,7 +34,7 @@
wmHelper: WindowManagerStateHelper?
) {
// do nothing (the app is focused automatically)
- waitAndAssertIMEShown(device, wmHelper)
+ waitIMEShown(device, wmHelper)
}
override fun open() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index d8091a9..83fddae 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -51,17 +51,17 @@
"was left in an unknown state (e.g. in split screen)"
}
editText.click()
- waitAndAssertIMEShown(device, wmHelper)
+ waitIMEShown(device, wmHelper)
}
- protected fun waitAndAssertIMEShown(
+ protected fun waitIMEShown(
device: UiDevice,
wmHelper: WindowManagerStateHelper? = null
) {
if (wmHelper == null) {
device.waitForIdle()
} else {
- require(wmHelper.waitImeWindowShown()) { "IME did not appear" }
+ wmHelper.waitImeWindowShown()
}
}
@@ -78,7 +78,7 @@
if (wmHelper == null) {
device.waitForIdle()
} else {
- require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
+ wmHelper.waitImeWindowGone()
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index b25bc99..dfb229d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -136,7 +136,7 @@
@Test
fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
- @Presubmit
+ @FlakyTest
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index d39044ab..7ba9db1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -20,6 +20,7 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -134,7 +135,7 @@
}
}
- @Presubmit
+ @FlakyTest
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 95b1d3c..be0357e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -153,7 +153,7 @@
testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
}
- @Presubmit
+ @FlakyTest
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index a9888b1..a3d1395 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -24,6 +25,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -56,6 +58,12 @@
}
}
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index a19a95d..62b9b81 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -80,6 +80,24 @@
super.navBarLayerRotatesAndScales()
}
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ super.statusBarLayerRotatesScales()
+ }
+
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ @FlakyTest
+ @Test
+ override fun focusChanges() {
+ super.focusChanges()
+ }
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index dcc64c9..38af8a7d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -24,6 +25,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -61,6 +63,18 @@
}
}
+ @FlakyTest
+ @Test
+ override fun focusChanges() {
+ super.focusChanges()
+ }
+
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index f037f1d..35ad597 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -24,7 +24,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,20 +60,6 @@
@Presubmit
@Test
- override fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
- super.navBarLayerRotatesAndScales()
- }
-
- @FlakyTest(bugId = 140855415)
- @Test
- fun navBarLayerRotatesAndScales_flaky() {
- Assume.assumeTrue(testSpec.isRotated)
- super.navBarLayerRotatesAndScales()
- }
-
- @Presubmit
- @Test
fun screenshotLayerBecomesInvisible() {
testSpec.assertLayers {
this.isVisible(testApp.getPackage())
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 6d2a923..2989035 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -18,6 +18,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
@@ -79,7 +80,7 @@
testSpec.navBarLayerIsAlwaysVisible()
}
- @Presubmit
+ @FlakyTest
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(
@@ -98,14 +99,14 @@
testSpec.statusBarLayerIsAlwaysVisible()
}
- @Presubmit
+ @FlakyTest
@Test
open fun statusBarLayerRotatesScales() {
testSpec.statusBarLayerRotatesScales(
testSpec.config.startRotation, testSpec.config.endRotation)
}
- @Presubmit
+ @FlakyTest
@Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
@@ -121,7 +122,7 @@
}
}
- @Presubmit
+ @FlakyTest
@Test
open fun noUncoveredRegions() {
testSpec.noUncoveredRegions(testSpec.config.startRotation,
@@ -134,19 +135,19 @@
testSpec.focusDoesNotChange()
}
- @Presubmit
+ @FlakyTest
@Test
open fun appLayerRotates_StartingPos() {
testSpec.assertLayersStart {
- this.coversExactly(startingPos, testApp.getPackage())
+ this.visibleRegion(testApp.getPackage()).coversExactly(startingPos)
}
}
- @Presubmit
+ @FlakyTest
@Test
open fun appLayerRotates_EndingPos() {
testSpec.assertLayersEnd {
- this.coversExactly(endingPos, testApp.getPackage())
+ this.visibleRegion(testApp.getPackage()).coversExactly(endingPos)
}
}
}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
index 340e6f9..7424157 100644
--- a/tests/net/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -198,4 +198,4 @@
cb.expectCallback<OnUnavailable>() { nr.getNetworkSpecifier() == specifier }
mCm.unregisterNetworkProvider(provider)
}
-}
\ No newline at end of file
+}
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 14dddcbd..e039ef0 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
@@ -73,7 +73,7 @@
import kotlin.test.fail
const val SERVICE_BIND_TIMEOUT_MS = 5_000L
-const val TEST_TIMEOUT_MS = 1_000L
+const val TEST_TIMEOUT_MS = 10_000L
/**
* Test that exercises an instrumented version of ConnectivityService against an instrumented
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index e2d43cb..6245e8542 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -41,6 +41,7 @@
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
+import android.net.NetworkScore;
import android.net.NetworkSpecifier;
import android.net.QosFilter;
import android.net.SocketKeepalive;
@@ -199,6 +200,11 @@
}
}
+ public void setScore(@NonNull final NetworkScore score) {
+ mScore = score.getLegacyInt();
+ mNetworkAgent.sendNetworkScore(score);
+ }
+
public void adjustScore(int change) {
mScore += change;
mNetworkAgent.sendNetworkScore(mScore);
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 36f205b..6cbdd25 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -41,10 +41,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java
index b7a42ec..fee65f0 100644
--- a/tests/net/java/android/net/VpnTransportInfoTest.java
+++ b/tests/net/java/android/net/VpnTransportInfoTest.java
@@ -16,6 +16,9 @@
package android.net;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
+
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static org.junit.Assert.assertEquals;
@@ -33,23 +36,33 @@
@Test
public void testParceling() {
- VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
- assertParcelSane(v, 1 /* fieldCount */);
+ VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345");
+ assertParcelSane(v, 2 /* fieldCount */);
}
@Test
public void testEqualsAndHashCode() {
- VpnTransportInfo v1 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
- VpnTransportInfo v2 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE);
- VpnTransportInfo v3 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
- VpnTransportInfo v4 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY);
- VpnTransportInfo v5 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM);
+ String session1 = "12345";
+ String session2 = "6789";
+ VpnTransportInfo v11 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
+ VpnTransportInfo v12 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, session1);
+ VpnTransportInfo v13 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
+ VpnTransportInfo v14 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session1);
+ VpnTransportInfo v15 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1);
+ VpnTransportInfo v21 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session2);
- assertNotEquals(v1, v2);
- assertNotEquals(v3, v4);
- assertNotEquals(v4, v5);
+ VpnTransportInfo v31 = v11.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
+ VpnTransportInfo v32 = v13.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
- assertEquals(v1, v3);
- assertEquals(v1.hashCode(), v3.hashCode());
+ assertNotEquals(v11, v12);
+ assertNotEquals(v13, v14);
+ assertNotEquals(v14, v15);
+ assertNotEquals(v14, v21);
+
+ assertEquals(v11, v13);
+ assertEquals(v31, v32);
+ assertEquals(v11.hashCode(), v13.hashCode());
+ assertEquals(REDACT_FOR_NETWORK_SETTINGS, v32.getApplicableRedactions());
+ assertEquals(session1, v15.makeCopy(REDACT_NONE).sessionId);
}
-}
\ No newline at end of file
+}
diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
index b62bdbc..5006d53 100644
--- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
+++ b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
@@ -101,7 +101,7 @@
// Check valid customization generates expected array.
val validRes = arrayOf("0,3", "1,0", "4,4")
- val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0)
+ val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0, 0)
val mockContext = getMockedContextWithStringArrayRes(
R.array.config_networkSupportedKeepaliveCount,
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 44298d4..8254f6b3 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -31,6 +31,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
@@ -94,6 +95,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+import static android.net.NetworkScore.KEEP_CONNECTED_FOR_HANDOVER;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
@@ -715,6 +717,9 @@
private int mProbesSucceeded;
private String mNmValidationRedirectUrl = null;
private boolean mNmProvNotificationRequested = false;
+ private Runnable mCreatedCallback;
+ private Runnable mUnwantedCallback;
+ private Runnable mDisconnectedCallback;
private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
// Contains the redirectUrl from networkStatus(). Before reading, wait for
@@ -769,6 +774,24 @@
mRedirectUrl = redirectUrl;
mNetworkStatusReceived.open();
}
+
+ @Override
+ public void onNetworkCreated() {
+ super.onNetworkCreated();
+ if (mCreatedCallback != null) mCreatedCallback.run();
+ }
+
+ @Override
+ public void onNetworkUnwanted() {
+ super.onNetworkUnwanted();
+ if (mUnwantedCallback != null) mUnwantedCallback.run();
+ }
+
+ @Override
+ public void onNetworkDestroyed() {
+ super.onNetworkDestroyed();
+ if (mDisconnectedCallback != null) mDisconnectedCallback.run();
+ }
};
assertEquals(na.getNetwork().netId, nmNetworkCaptor.getValue().netId);
@@ -970,6 +993,18 @@
p.timestampMillis = DATA_STALL_TIMESTAMP;
mNmCallbacks.notifyDataStallSuspected(p);
}
+
+ public void setCreatedCallback(Runnable r) {
+ mCreatedCallback = r;
+ }
+
+ public void setUnwantedCallback(Runnable r) {
+ mUnwantedCallback = r;
+ }
+
+ public void setDisconnectedCallback(Runnable r) {
+ mDisconnectedCallback = r;
+ }
}
/**
@@ -977,8 +1012,6 @@
* operations have been processed and test for them.
*/
private static class MockNetworkFactory extends NetworkFactory {
- private final ConditionVariable mNetworkStartedCV = new ConditionVariable();
- private final ConditionVariable mNetworkStoppedCV = new ConditionVariable();
private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false);
static class RequestEntry {
@@ -990,11 +1023,8 @@
}
static final class Add extends RequestEntry {
- public final int factorySerialNumber;
-
- Add(@NonNull final NetworkRequest request, final int factorySerialNumber) {
+ Add(@NonNull final NetworkRequest request) {
super(request);
- this.factorySerialNumber = factorySerialNumber;
}
}
@@ -1003,6 +1033,11 @@
super(request);
}
}
+
+ @Override
+ public String toString() {
+ return "RequestEntry [ " + getClass().getName() + " : " + request + " ]";
+ }
}
// History of received requests adds and removes.
@@ -1014,7 +1049,6 @@
return obj;
}
-
public RequestEntry.Add expectRequestAdd() {
return failIfNull((RequestEntry.Add) mRequestHistory.poll(TIMEOUT_MS,
it -> it instanceof RequestEntry.Add), "Expected request add");
@@ -1054,40 +1088,28 @@
protected void startNetwork() {
mNetworkStarted.set(true);
- mNetworkStartedCV.open();
}
protected void stopNetwork() {
mNetworkStarted.set(false);
- mNetworkStoppedCV.open();
}
public boolean getMyStartRequested() {
return mNetworkStarted.get();
}
- public ConditionVariable getNetworkStartedCV() {
- mNetworkStartedCV.close();
- return mNetworkStartedCV;
- }
-
- public ConditionVariable getNetworkStoppedCV() {
- mNetworkStoppedCV.close();
- return mNetworkStoppedCV;
- }
@Override
- protected void handleAddRequest(NetworkRequest request, int score,
- int factorySerialNumber) {
+ protected void needNetworkFor(NetworkRequest request) {
mNetworkRequests.put(request.requestId, request);
- super.handleAddRequest(request, score, factorySerialNumber);
- mRequestHistory.add(new RequestEntry.Add(request, factorySerialNumber));
+ super.needNetworkFor(request);
+ mRequestHistory.add(new RequestEntry.Add(request));
}
@Override
- protected void handleRemoveRequest(NetworkRequest request) {
+ protected void releaseNetworkFor(NetworkRequest request) {
mNetworkRequests.remove(request.requestId);
- super.handleRemoveRequest(request);
+ super.releaseNetworkFor(request);
mRequestHistory.add(new RequestEntry.Remove(request));
}
@@ -1193,10 +1215,12 @@
if (mAgentRegistered) throw new IllegalStateException("already registered");
updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent");
mConfig = new VpnConfig();
+ mConfig.session = "MySession12345";
setUids(uids);
if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
mInterface = VPN_IFNAME;
- mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+ mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType(),
+ mConfig.session));
mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
mNetworkCapabilities);
mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
@@ -1373,10 +1397,21 @@
}
private void mockUidNetworkingBlocked() {
- doAnswer(i -> NetworkPolicyManager.isUidBlocked(mBlockedReasons, i.getArgument(1))
+ doAnswer(i -> isUidBlocked(mBlockedReasons, i.getArgument(1))
).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
}
+ private boolean isUidBlocked(int blockedReasons, boolean meteredNetwork) {
+ final int blockedOnAllNetworksReason = (blockedReasons & ~BLOCKED_METERED_REASON_MASK);
+ if (blockedOnAllNetworksReason != BLOCKED_REASON_NONE) {
+ return true;
+ }
+ if (meteredNetwork) {
+ return blockedReasons != BLOCKED_REASON_NONE;
+ }
+ return false;
+ }
+
private void setBlockedReasonChanged(int blockedReasons) {
mBlockedReasons = blockedReasons;
mPolicyCallback.onUidBlockedReasonChanged(Process.myUid(), blockedReasons);
@@ -2800,6 +2835,94 @@
}
@Test
+ public void testNetworkAgentCallbacks() throws Exception {
+ // Keeps track of the order of events that happen in this test.
+ final LinkedBlockingQueue<String> eventOrder = new LinkedBlockingQueue<>();
+
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ final AtomicReference<Network> wifiNetwork = new AtomicReference<>();
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+
+ // Expectations for state when various callbacks fire. These expectations run on the handler
+ // thread and not on the test thread because they need to prevent the handler thread from
+ // advancing while they examine state.
+
+ // 1. When onCreated fires, netd has been told to create the network.
+ mWiFiNetworkAgent.setCreatedCallback(() -> {
+ eventOrder.offer("onNetworkCreated");
+ wifiNetwork.set(mWiFiNetworkAgent.getNetwork());
+ assertNotNull(wifiNetwork.get());
+ try {
+ verify(mMockNetd).networkCreatePhysical(wifiNetwork.get().getNetId(),
+ INetd.PERMISSION_NONE);
+ } catch (RemoteException impossible) {
+ fail();
+ }
+ });
+
+ // 2. onNetworkUnwanted isn't precisely ordered with respect to any particular events. Just
+ // check that it is fired at some point after disconnect.
+ mWiFiNetworkAgent.setUnwantedCallback(() -> eventOrder.offer("onNetworkUnwanted"));
+
+ // 3. While the teardown timer is running, connectivity APIs report the network is gone, but
+ // netd has not yet been told to destroy it.
+ final Runnable duringTeardown = () -> {
+ eventOrder.offer("timePasses");
+ assertNull(mCm.getLinkProperties(wifiNetwork.get()));
+ try {
+ verify(mMockNetd, never()).networkDestroy(wifiNetwork.get().getNetId());
+ } catch (RemoteException impossible) {
+ fail();
+ }
+ };
+
+ // 4. After onNetworkDisconnected is called, connectivity APIs report the network is gone,
+ // and netd has been told to destroy it.
+ mWiFiNetworkAgent.setDisconnectedCallback(() -> {
+ eventOrder.offer("onNetworkDisconnected");
+ assertNull(mCm.getLinkProperties(wifiNetwork.get()));
+ try {
+ verify(mMockNetd).networkDestroy(wifiNetwork.get().getNetId());
+ } catch (RemoteException impossible) {
+ fail();
+ }
+ });
+
+ // Connect a network, and file a request for it after it has come up, to ensure the nascent
+ // timer is cleared and the test does not have to wait for it. Filing the request after the
+ // network has come up is necessary because ConnectivityService does not appear to clear the
+ // nascent timer if the first request satisfied by the network was filed before the network
+ // connected.
+ // TODO: fix this bug, file the request before connecting, and remove the waitForIdle.
+ mWiFiNetworkAgent.connectWithoutInternet();
+ waitForIdle();
+ mCm.requestNetwork(request, callback);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Set teardown delay and make sure CS has processed it.
+ mWiFiNetworkAgent.getNetworkAgent().setTeardownDelayMs(300);
+ waitForIdle();
+
+ // Post the duringTeardown lambda to the handler so it fires while teardown is in progress.
+ // The delay must be long enough it will run after the unregisterNetworkCallback has torn
+ // down the network and started the teardown timer, and short enough that the lambda is
+ // scheduled to run before the teardown timer.
+ final Handler h = new Handler(mCsHandlerThread.getLooper());
+ h.postDelayed(duringTeardown, 150);
+
+ // Disconnect the network and check that events happened in the right order.
+ mCm.unregisterNetworkCallback(callback);
+ assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals("onNetworkUnwanted", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals("timePasses", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals("onNetworkDisconnected", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ mCm.unregisterNetworkCallback(callback);
+ }
+
+ @Test
public void testExplicitlySelected() throws Exception {
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
@@ -2919,25 +3042,23 @@
handlerThread.start();
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
mServiceContext, "testFactory", filter, mCsHandlerThread);
- testFactory.setScoreFilter(40);
- ConditionVariable cv = testFactory.getNetworkStartedCV();
+ testFactory.setScoreFilter(45);
testFactory.register();
- testFactory.expectRequestAdd();
- testFactory.assertRequestCountEquals(1);
- int expectedRequestCount = 1;
- NetworkCallback networkCallback = null;
- // For non-INTERNET capabilities we cannot rely on the default request being present, so
- // add one.
+
+ final NetworkCallback networkCallback;
if (capability != NET_CAPABILITY_INTERNET) {
+ // If the capability passed in argument is part of the default request, then the
+ // factory will see the default request. Otherwise the filter will prevent the
+ // factory from seeing it. In that case, add a request so it can be tested.
assertFalse(testFactory.getMyStartRequested());
NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build();
networkCallback = new NetworkCallback();
mCm.requestNetwork(request, networkCallback);
- expectedRequestCount++;
- testFactory.expectRequestAdd();
+ } else {
+ networkCallback = null;
}
- waitFor(cv);
- testFactory.assertRequestCountEquals(expectedRequestCount);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
assertTrue(testFactory.getMyStartRequested());
// Now bring in a higher scored network.
@@ -2946,20 +3067,45 @@
// own NetworkRequest during startup, just bump up the score to cancel out the
// unvalidated penalty.
testAgent.adjustScore(40);
- cv = testFactory.getNetworkStoppedCV();
- // When testAgent connects, ConnectivityService will re-send us all current requests with
- // the new score. There are expectedRequestCount such requests, and we must wait for all of
- // them.
+ // When testAgent connects, because of its 50 score (50 for cell + 40 adjustment score
+ // - 40 penalty for not being validated), it will beat the testFactory's offer, so
+ // the request will be removed.
testAgent.connect(false);
testAgent.addCapability(capability);
- waitFor(cv);
- testFactory.expectRequestAdds(expectedRequestCount);
- testFactory.assertRequestCountEquals(expectedRequestCount);
+ testFactory.expectRequestRemove();
+ testFactory.assertRequestCountEquals(0);
assertFalse(testFactory.getMyStartRequested());
+ // Add a request and make sure it's not sent to the factory, because the agent
+ // is satisfying it better.
+ final NetworkCallback cb = new ConnectivityManager.NetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder().addCapability(capability).build(), cb);
+ expectNoRequestChanged(testFactory);
+ testFactory.assertRequestCountEquals(0);
+ assertFalse(testFactory.getMyStartRequested());
+
+ // Make the test agent weak enough to have the exact same score as the
+ // factory (50 for cell + 40 adjustment -40 validation penalty - 5 adjustment). Make sure
+ // the factory doesn't see the request.
+ testAgent.adjustScore(-5);
+ expectNoRequestChanged(testFactory);
+ assertFalse(testFactory.getMyStartRequested());
+
+ // Make the test agent weak enough to see the two requests (the one that was just sent,
+ // and either the default one or the one sent at the top of this test if the default
+ // won't be seen).
+ testAgent.adjustScore(-45);
+ testFactory.expectRequestAdds(2);
+ testFactory.assertRequestCountEquals(2);
+ assertTrue(testFactory.getMyStartRequested());
+
+ // Now unregister and make sure the request is removed.
+ mCm.unregisterNetworkCallback(cb);
+ testFactory.expectRequestRemove();
+
// Bring in a bunch of requests.
- assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
+ assertEquals(1, testFactory.getMyRequestCount());
ConnectivityManager.NetworkCallback[] networkCallbacks =
new ConnectivityManager.NetworkCallback[10];
for (int i = 0; i< networkCallbacks.length; i++) {
@@ -2969,24 +3115,28 @@
mCm.requestNetwork(builder.build(), networkCallbacks[i]);
}
testFactory.expectRequestAdds(10);
- testFactory.assertRequestCountEquals(10 + expectedRequestCount);
- assertFalse(testFactory.getMyStartRequested());
+ testFactory.assertRequestCountEquals(11); // +1 for the default/test specific request
+ assertTrue(testFactory.getMyStartRequested());
// Remove the requests.
for (int i = 0; i < networkCallbacks.length; i++) {
mCm.unregisterNetworkCallback(networkCallbacks[i]);
}
testFactory.expectRequestRemoves(10);
- testFactory.assertRequestCountEquals(expectedRequestCount);
+ testFactory.assertRequestCountEquals(1);
+ assertTrue(testFactory.getMyStartRequested());
+
+ // Adjust the agent score up again. Expect the request to be withdrawn.
+ testAgent.adjustScore(50);
+ testFactory.expectRequestRemove();
+ testFactory.assertRequestCountEquals(0);
assertFalse(testFactory.getMyStartRequested());
// Drop the higher scored network.
- cv = testFactory.getNetworkStartedCV();
testAgent.disconnect();
- waitFor(cv);
- testFactory.expectRequestAdds(expectedRequestCount);
- testFactory.assertRequestCountEquals(expectedRequestCount);
- assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
+ assertEquals(1, testFactory.getMyRequestCount());
assertTrue(testFactory.getMyStartRequested());
testFactory.terminate();
@@ -3016,9 +3166,34 @@
}
@Test
- public void testNetworkFactoryUnregister() throws Exception {
+ public void testRegisterIgnoringScore() throws Exception {
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.adjustScore(30); // score = 60 (wifi) + 30 = 90
+ mWiFiNetworkAgent.connect(true /* validated */);
+
+ // Make sure the factory sees the default network
final NetworkCapabilities filter = new NetworkCapabilities();
- filter.clearAll();
+ filter.addCapability(NET_CAPABILITY_INTERNET);
+ filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+ final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
+ handlerThread.start();
+ final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "testFactory", filter, mCsHandlerThread);
+ testFactory.registerIgnoringScore();
+ testFactory.expectRequestAdd();
+
+ mWiFiNetworkAgent.adjustScore(20); // exceed the maximum score
+ expectNoRequestChanged(testFactory); // still seeing the request
+
+ mWiFiNetworkAgent.disconnect();
+ }
+
+ @Test
+ public void testNetworkFactoryUnregister() throws Exception {
+ // Make sure the factory sees the default network
+ final NetworkCapabilities filter = new NetworkCapabilities();
+ filter.addCapability(NET_CAPABILITY_INTERNET);
+ filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
handlerThread.start();
@@ -4282,6 +4457,7 @@
testFactory.register();
try {
+ // Expect the factory to receive the default network request.
testFactory.expectRequestAdd();
testFactory.assertRequestCountEquals(1);
assertTrue(testFactory.getMyStartRequested());
@@ -4290,25 +4466,44 @@
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
// Score 60 - 40 penalty for not validated yet, then 60 when it validates
mWiFiNetworkAgent.connect(true);
- // Default request and mobile always on request
- testFactory.expectRequestAdds(2);
+ // The network connects with a low score, so the offer can still beat it and
+ // nothing happens. Then the network validates, and the offer with its filter score
+ // of 40 can no longer beat it and the request is removed.
+ testFactory.expectRequestRemove();
+ testFactory.assertRequestCountEquals(0);
+
assertFalse(testFactory.getMyStartRequested());
- // Turn on mobile data always on. The factory starts looking again.
+ // Turn on mobile data always on. This request will not match the wifi request, so
+ // it will be sent to the test factory whose filters allow to see it.
setAlwaysOnNetworks(true);
testFactory.expectRequestAdd();
- testFactory.assertRequestCountEquals(2);
+ testFactory.assertRequestCountEquals(1);
assertTrue(testFactory.getMyStartRequested());
// Bring up cell data and check that the factory stops looking.
assertLength(1, mCm.getAllNetworks());
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- testFactory.expectRequestAdds(2); // Unvalidated and validated
- testFactory.assertRequestCountEquals(2);
- // The cell network outscores the factory filter, so start is not requested.
+ mCellNetworkAgent.connect(false);
+ cellNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent, false, false, false,
+ TEST_CALLBACK_TIMEOUT_MS);
+ // When cell connects, it will satisfy the "mobile always on request" right away
+ // by virtue of being the only network that can satisfy the request. However, its
+ // score is low (50 - 40 = 10) so the test factory can still hope to beat it.
+ expectNoRequestChanged(testFactory);
+
+ // Next, cell validates. This gives it a score of 50 and the test factory can't
+ // hope to beat that according to its filters. It will see the message that its
+ // offer is now unnecessary.
+ mCellNetworkAgent.setNetworkValid(true);
+ // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is
+ // validated – see testPartialConnectivity.
+ mCm.reportNetworkConnectivity(mCellNetworkAgent.getNetwork(), true);
+ cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent);
+ testFactory.expectRequestRemove();
+ testFactory.assertRequestCountEquals(0);
+ // Accordingly, the factory shouldn't be started.
assertFalse(testFactory.getMyStartRequested());
// Check that cell data stays up.
@@ -4316,10 +4511,27 @@
verifyActiveNetwork(TRANSPORT_WIFI);
assertLength(2, mCm.getAllNetworks());
- // Turn off mobile data always on and expect the request to disappear...
- setAlwaysOnNetworks(false);
- testFactory.expectRequestRemove();
+ // Cell disconnects. There is still the "mobile data always on" request outstanding,
+ // and the test factory should see it now that it isn't hopelessly outscored.
+ mCellNetworkAgent.disconnect();
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ assertLength(1, mCm.getAllNetworks());
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
+ // Reconnect cell validated, see the request disappear again. Then withdraw the
+ // mobile always on request. This will tear down cell, and there shouldn't be a
+ // blip where the test factory briefly sees the request or anything.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ assertLength(2, mCm.getAllNetworks());
+ testFactory.expectRequestRemove();
+ testFactory.assertRequestCountEquals(0);
+ setAlwaysOnNetworks(false);
+ expectNoRequestChanged(testFactory);
+ testFactory.assertRequestCountEquals(0);
+ assertFalse(testFactory.getMyStartRequested());
// ... and cell data to be torn down after nascent network timeout.
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS);
@@ -4622,7 +4834,8 @@
handlerThread.start();
NetworkCapabilities filter = new NetworkCapabilities()
.addTransportType(TRANSPORT_WIFI)
- .addCapability(NET_CAPABILITY_INTERNET);
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
mServiceContext, "testFactory", filter, mCsHandlerThread);
testFactory.setScoreFilter(40);
@@ -4631,32 +4844,43 @@
testFactory.register();
testFactory.expectRequestAdd();
- // Now file the test request and expect it.
- mCm.requestNetwork(nr, networkCallback);
- final NetworkRequest newRequest = testFactory.expectRequestAdd().request;
+ try {
+ // Now file the test request and expect it.
+ mCm.requestNetwork(nr, networkCallback);
+ final NetworkRequest newRequest = testFactory.expectRequestAdd().request;
- if (preUnregister) {
- mCm.unregisterNetworkCallback(networkCallback);
+ if (preUnregister) {
+ mCm.unregisterNetworkCallback(networkCallback);
- // Simulate the factory releasing the request as unfulfillable: no-op since
- // the callback has already been unregistered (but a test that no exceptions are
- // thrown).
- testFactory.triggerUnfulfillable(newRequest);
- } else {
- // Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
- testFactory.triggerUnfulfillable(newRequest);
+ // The request has been released : the factory should see it removed
+ // immediately.
+ testFactory.expectRequestRemove();
- networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
+ // Simulate the factory releasing the request as unfulfillable: no-op since
+ // the callback has already been unregistered (but a test that no exceptions are
+ // thrown).
+ testFactory.triggerUnfulfillable(newRequest);
+ } else {
+ // Simulate the factory releasing the request as unfulfillable and expect
+ // onUnavailable!
+ testFactory.triggerUnfulfillable(newRequest);
- // unregister network callback - a no-op (since already freed by the
- // on-unavailable), but should not fail or throw exceptions.
- mCm.unregisterNetworkCallback(networkCallback);
+ networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
+
+ // Declaring a request unfulfillable releases it automatically.
+ testFactory.expectRequestRemove();
+
+ // unregister network callback - a no-op (since already freed by the
+ // on-unavailable), but should not fail or throw exceptions.
+ mCm.unregisterNetworkCallback(networkCallback);
+
+ // The factory should not see any further removal, as this request has
+ // already been removed.
+ }
+ } finally {
+ testFactory.terminate();
+ handlerThread.quit();
}
-
- testFactory.expectRequestRemove();
-
- testFactory.terminate();
- handlerThread.quit();
}
private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
@@ -7278,6 +7502,20 @@
mMockVpn.disconnect();
}
+ private class DetailedBlockedStatusCallback extends TestNetworkCallback {
+ public void expectAvailableThenValidatedCallbacks(HasNetwork n, int blockedStatus) {
+ super.expectAvailableThenValidatedCallbacks(n.getNetwork(), blockedStatus, TIMEOUT_MS);
+ }
+ public void expectBlockedStatusCallback(HasNetwork n, int blockedStatus) {
+ // This doesn't work:
+ // super.expectBlockedStatusCallback(blockedStatus, n.getNetwork());
+ super.expectBlockedStatusCallback(blockedStatus, n.getNetwork(), TIMEOUT_MS);
+ }
+ public void onBlockedStatusChanged(Network network, int blockedReasons) {
+ getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons));
+ }
+ }
+
@Test
public void testNetworkBlockedStatus() throws Exception {
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
@@ -7285,11 +7523,16 @@
.addTransportType(TRANSPORT_CELLULAR)
.build();
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+ final DetailedBlockedStatusCallback detailedCallback = new DetailedBlockedStatusCallback();
+ mCm.registerNetworkCallback(cellRequest, detailedCallback);
+
mockUidNetworkingBlocked();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ detailedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent,
+ BLOCKED_REASON_NONE);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
@@ -7297,17 +7540,23 @@
setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
+ BLOCKED_REASON_BATTERY_SAVER);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertExtraInfoFromCmBlocked(mCellNetworkAgent);
- // ConnectivityService should cache it not to invoke the callback again.
+ // If blocked state does not change but blocked reason does, the boolean callback is called.
+ // TODO: investigate de-duplicating.
setBlockedReasonChanged(BLOCKED_METERED_REASON_USER_RESTRICTED);
- cellNetworkCallback.assertNoCallback();
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
+ BLOCKED_METERED_REASON_USER_RESTRICTED);
setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
@@ -7315,6 +7564,8 @@
setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
+ BLOCKED_METERED_REASON_DATA_SAVER);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
@@ -7324,6 +7575,8 @@
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ detailedCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+ detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
@@ -7333,6 +7586,10 @@
cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ detailedCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
+ mCellNetworkAgent);
+ detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
+ BLOCKED_METERED_REASON_DATA_SAVER);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
@@ -7340,6 +7597,7 @@
setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
@@ -7347,10 +7605,13 @@
setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.assertNoCallback();
+ detailedCallback.assertNoCallback();
// Restrict background data. Networking is not blocked because the network is unmetered.
setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent,
+ BLOCKED_METERED_REASON_DATA_SAVER);
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
@@ -7360,12 +7621,14 @@
setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE);
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertExtraInfoFromCmPresent(mCellNetworkAgent);
setBlockedReasonChanged(BLOCKED_REASON_NONE);
cellNetworkCallback.assertNoCallback();
+ detailedCallback.assertNoCallback();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
@@ -9851,6 +10114,83 @@
}
}
+ @Test
+ public void testKeepConnected() throws Exception {
+ setAlwaysOnNetworks(false);
+ registerDefaultNetworkCallbacks();
+ final TestNetworkCallback allNetworksCb = new TestNetworkCallback();
+ final NetworkRequest allNetworksRequest = new NetworkRequest.Builder().clearCapabilities()
+ .build();
+ mCm.registerNetworkCallback(allNetworksRequest, allNetworksCb);
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true /* validated */);
+
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ allNetworksCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true /* validated */);
+
+ mDefaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ // While the default callback doesn't see the network before it's validated, the listen
+ // sees the network come up and validate later
+ allNetworksCb.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ allNetworksCb.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+ allNetworksCb.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+ TEST_LINGER_DELAY_MS * 2);
+
+ // The cell network has disconnected (see LOST above) because it was outscored and
+ // had no requests (see setAlwaysOnNetworks(false) above)
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ final NetworkScore score = new NetworkScore.Builder().setLegacyInt(30).build();
+ mCellNetworkAgent.setScore(score);
+ mCellNetworkAgent.connect(false /* validated */);
+
+ // The cell network gets torn down right away.
+ allNetworksCb.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+ TEST_NASCENT_DELAY_MS * 2);
+ allNetworksCb.assertNoCallback();
+
+ // Now create a cell network with KEEP_CONNECTED_FOR_HANDOVER and make sure it's
+ // not disconnected immediately when outscored.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ final NetworkScore scoreKeepup = new NetworkScore.Builder().setLegacyInt(30)
+ .setKeepConnectedReason(KEEP_CONNECTED_FOR_HANDOVER).build();
+ mCellNetworkAgent.setScore(scoreKeepup);
+ mCellNetworkAgent.connect(true /* validated */);
+
+ allNetworksCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.assertNoCallback();
+
+ mWiFiNetworkAgent.disconnect();
+
+ allNetworksCb.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+
+ // Reconnect a WiFi network and make sure the cell network is still not torn down.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true /* validated */);
+
+ allNetworksCb.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+ // Now remove the reason to keep connected and make sure the network lingers and is
+ // torn down.
+ mCellNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(30).build());
+ allNetworksCb.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent,
+ TEST_NASCENT_DELAY_MS * 2);
+ allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+ TEST_LINGER_DELAY_MS * 2);
+ mDefaultNetworkCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(allNetworksCb);
+ // mDefaultNetworkCallback will be unregistered by tearDown()
+ }
+
private class QosCallbackMockHelper {
@NonNull public final QosFilter mFilter;
@NonNull public final IQosCallback mCallback;
@@ -11210,6 +11550,115 @@
// default callbacks will be unregistered in tearDown
}
+ @Test
+ public void testNetworkFactoryRequestsWithMultilayerRequest()
+ throws Exception {
+ // First use OEM_PAID preference to create a multi-layer request : 1. listen for
+ // unmetered, 2. request network with cap OEM_PAID, 3, request the default network for
+ // fallback.
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+ setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+
+ final HandlerThread handlerThread = new HandlerThread("MockFactory");
+ handlerThread.start();
+ NetworkCapabilities internetFilter = new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+ final MockNetworkFactory internetFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "internetFactory", internetFilter, mCsHandlerThread);
+ internetFactory.setScoreFilter(40);
+ internetFactory.register();
+ // Default internet request & 3rd (fallback) request in OEM_PAID NRI. The unmetered request
+ // is never sent to factories (it's a LISTEN, not requestable) and the OEM_PAID request
+ // doesn't match the internetFactory filter.
+ internetFactory.expectRequestAdds(2);
+
+ NetworkCapabilities oemPaidFilter = new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_OEM_PAID)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ final MockNetworkFactory oemPaidFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "oemPaidFactory", oemPaidFilter, mCsHandlerThread);
+ oemPaidFactory.setScoreFilter(40);
+ oemPaidFactory.register();
+ oemPaidFactory.expectRequestAdd(); // Because nobody satisfies the request
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ // A network connected that satisfies the default internet request. For the OEM_PAID
+ // preference, this is not as good as an OEM_PAID network, so even if the score of
+ // the network is better than the factory announced, it still should try to bring up
+ // the network.
+ expectNoRequestChanged(oemPaidFactory);
+ oemPaidFactory.assertRequestCountEquals(1);
+ // The internet factory however is outscored, and should lose its requests.
+ internetFactory.expectRequestRemoves(2);
+ internetFactory.assertRequestCountEquals(0);
+
+ final NetworkCapabilities oemPaidNc = new NetworkCapabilities();
+ oemPaidNc.addCapability(NET_CAPABILITY_OEM_PAID);
+ oemPaidNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ final TestNetworkAgentWrapper oemPaidAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR,
+ new LinkProperties(), oemPaidNc);
+ oemPaidAgent.connect(true);
+
+ // The oemPaidAgent has score 50 (default for cell) so it beats what the oemPaidFactory can
+ // provide, therefore it loses the request.
+ oemPaidFactory.expectRequestRemove();
+ oemPaidFactory.assertRequestCountEquals(0);
+ expectNoRequestChanged(internetFactory);
+ internetFactory.assertRequestCountEquals(0);
+
+ oemPaidAgent.adjustScore(-30);
+ // Now the that the agent is weak, the oemPaidFactory can beat the existing network for the
+ // OEM_PAID request. The internet factory however can't beat a network that has OEM_PAID
+ // for the preference request, so it doesn't see the request.
+ oemPaidFactory.expectRequestAdd();
+ oemPaidFactory.assertRequestCountEquals(1);
+ expectNoRequestChanged(internetFactory);
+ internetFactory.assertRequestCountEquals(0);
+
+ mCellNetworkAgent.disconnect();
+ // The network satisfying the default internet request has disconnected, so the
+ // internetFactory sees the default request again. However there is a network with OEM_PAID
+ // connected, so the 2nd OEM_PAID req is already satisfied, so the oemPaidFactory doesn't
+ // care about networks that don't have OEM_PAID.
+ expectNoRequestChanged(oemPaidFactory);
+ oemPaidFactory.assertRequestCountEquals(1);
+ internetFactory.expectRequestAdd();
+ internetFactory.assertRequestCountEquals(1);
+
+ // Cell connects again, still with score 50. Back to the previous state.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ expectNoRequestChanged(oemPaidFactory);
+ oemPaidFactory.assertRequestCountEquals(1);
+ internetFactory.expectRequestRemove();
+ internetFactory.assertRequestCountEquals(0);
+
+ // Now WiFi connects and it's unmetered, but it's weaker than cell.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.adjustScore(-30); // Not the best Internet network, but unmetered
+ mWiFiNetworkAgent.connect(true);
+
+ // The OEM_PAID preference prefers an unmetered network to an OEM_PAID network, so
+ // the oemPaidFactory can't beat this no matter how high its score.
+ oemPaidFactory.expectRequestRemove();
+ expectNoRequestChanged(internetFactory);
+
+ mCellNetworkAgent.disconnect();
+ // Now that the best internet network (cell, with its 50 score compared to 30 for WiFi
+ // at this point), the default internet request is satisfied by a network worse than
+ // the internetFactory announced, so it gets the request. However, there is still an
+ // unmetered network, so the oemPaidNetworkFactory still can't beat this.
+ expectNoRequestChanged(oemPaidFactory);
+ internetFactory.expectRequestAdd();
+ }
+
/**
* Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order:
* NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID
@@ -11504,11 +11953,11 @@
testFactory.setScoreFilter(40);
try {
- // Register the factory and expect it will see default request, because all requests
- // are sent to all factories.
+ // Register the factory. It doesn't see the default request because its filter does
+ // not include INTERNET.
testFactory.register();
- testFactory.expectRequestAdd();
- testFactory.assertRequestCountEquals(1);
+ expectNoRequestChanged(testFactory);
+ testFactory.assertRequestCountEquals(0);
// The factory won't try to start the network since the default request doesn't
// match the filter (no INTERNET capability).
assertFalse(testFactory.getMyStartRequested());
@@ -11521,7 +11970,7 @@
bestMatchingCb, mCsHandlerThread.getThreadHandler());
bestMatchingCb.assertNoCallback();
expectNoRequestChanged(testFactory);
- testFactory.assertRequestCountEquals(1);
+ testFactory.assertRequestCountEquals(0);
assertFalse(testFactory.getMyStartRequested());
// Fire a normal mms request, verify the factory will only see the request.
@@ -11530,13 +11979,13 @@
.addCapability(NET_CAPABILITY_MMS).build();
mCm.requestNetwork(mmsRequest, mmsNetworkCallback);
testFactory.expectRequestAdd();
- testFactory.assertRequestCountEquals(2);
+ testFactory.assertRequestCountEquals(1);
assertTrue(testFactory.getMyStartRequested());
// Unregister best matching callback, verify factory see no change.
mCm.unregisterNetworkCallback(bestMatchingCb);
expectNoRequestChanged(testFactory);
- testFactory.assertRequestCountEquals(2);
+ testFactory.assertRequestCountEquals(1);
assertTrue(testFactory.getMyStartRequested());
} finally {
testFactory.terminate();
diff --git a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt b/tests/net/java/com/android/server/connectivity/FullScoreTest.kt
index eb3b4df..2864bc7 100644
--- a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt
+++ b/tests/net/java/com/android/server/connectivity/FullScoreTest.kt
@@ -18,6 +18,7 @@
import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities
+import android.net.NetworkScore.KEEP_CONNECTED_NONE
import android.text.TextUtils
import android.util.ArraySet
import androidx.test.filters.SmallTest
@@ -60,11 +61,11 @@
@Test
fun testGetLegacyInt() {
- val ns = FullScore(50, 0L /* policy */)
+ val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE)
assertEquals(10, ns.legacyInt) // -40 penalty for not being validated
assertEquals(50, ns.legacyIntAsValidated)
- val vpnNs = FullScore(101, 0L /* policy */).withPolicies(vpn = true)
+ val vpnNs = FullScore(101, 0L /* policy */, KEEP_CONNECTED_NONE).withPolicies(vpn = true)
assertEquals(101, vpnNs.legacyInt) // VPNs are not subject to unvalidation penalty
assertEquals(101, vpnNs.legacyIntAsValidated)
assertEquals(101, vpnNs.withPolicies(validated = true).legacyInt)
@@ -83,7 +84,7 @@
@Test
fun testToString() {
- val string = FullScore(10, 0L /* policy */)
+ val string = FullScore(10, 0L /* policy */, KEEP_CONNECTED_NONE)
.withPolicies(vpn = true, acceptUnvalidated = true).toString()
assertTrue(string.contains("Score(10"), string)
assertTrue(string.contains("ACCEPT_UNVALIDATED"), string)
@@ -107,7 +108,7 @@
@Test
fun testHasPolicy() {
- val ns = FullScore(50, 0L /* policy */)
+ val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE)
assertFalse(ns.hasPolicy(POLICY_IS_VALIDATED))
assertFalse(ns.hasPolicy(POLICY_IS_VPN))
assertFalse(ns.hasPolicy(POLICY_EVER_USER_SELECTED))
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 97c65eb..8b072c4 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -83,10 +83,10 @@
IpConnectivityMetrics mService;
NetdEventListenerService mNetdListener;
- final NetworkCapabilities mNcWifi = new NetworkCapabilities.Builder()
+ private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
- final NetworkCapabilities mNcCell = new NetworkCapabilities.Builder()
+ private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build();
@@ -590,7 +590,7 @@
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mCm).registerNetworkCallback(any(), networkCallback.capture());
networkCallback.getValue().onCapabilitiesChanged(new Network(netId),
- netId == 100 ? mNcWifi : mNcCell);
+ netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL);
}
void connectEvent(int netId, int error, int latencyMs, String ipAddr) throws Exception {
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 52975e4..50aaaee 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -63,10 +63,10 @@
NetdEventListenerService mService;
ConnectivityManager mCm;
- final NetworkCapabilities mNcWifi = new NetworkCapabilities.Builder()
+ private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
- final NetworkCapabilities mNcCell = new NetworkCapabilities.Builder()
+ private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build();
@@ -475,7 +475,7 @@
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mCm).registerNetworkCallback(any(), networkCallback.capture());
networkCallback.getValue().onCapabilitiesChanged(new Network(netId),
- netId == 100 ? mNcWifi : mNcCell);
+ netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL);
}
Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
diff --git a/tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt b/tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt
new file mode 100644
index 0000000..409f8c3
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity
+
+import android.net.INetworkOfferCallback
+import android.net.NetworkCapabilities
+import android.net.NetworkRequest
+import android.net.NetworkScore.KEEP_CONNECTED_NONE
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+const val POLICY_NONE = 0L
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetworkOfferTest {
+ val mockCallback = mock(INetworkOfferCallback::class.java)
+
+ @Test
+ fun testOfferNeededUnneeded() {
+ val score = FullScore(50, POLICY_NONE, KEEP_CONNECTED_NONE)
+ val offer = NetworkOffer(score, NetworkCapabilities.Builder().build(), mockCallback,
+ 1 /* providerId */)
+ val request1 = mock(NetworkRequest::class.java)
+ val request2 = mock(NetworkRequest::class.java)
+ offer.onNetworkNeeded(request1)
+ verify(mockCallback).onNetworkNeeded(eq(request1))
+ assertTrue(offer.neededFor(request1))
+ assertFalse(offer.neededFor(request2))
+
+ offer.onNetworkNeeded(request2)
+ verify(mockCallback).onNetworkNeeded(eq(request2))
+ assertTrue(offer.neededFor(request1))
+ assertTrue(offer.neededFor(request2))
+
+ // Note that the framework never calls onNetworkNeeded multiple times with the same
+ // request without calling onNetworkUnneeded first. It would be incorrect usage and the
+ // behavior would be undefined, so there is nothing to test.
+
+ offer.onNetworkUnneeded(request1)
+ verify(mockCallback).onNetworkUnneeded(eq(request1))
+ assertFalse(offer.neededFor(request1))
+ assertTrue(offer.neededFor(request2))
+
+ offer.onNetworkUnneeded(request2)
+ verify(mockCallback).onNetworkUnneeded(eq(request2))
+ assertFalse(offer.neededFor(request1))
+ assertFalse(offer.neededFor(request2))
+ }
+}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 9334e2c..eeeb4fb 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -89,6 +89,7 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.UnderlyingNetworkInfo;
+import android.net.TelephonyNetworkSpecifier;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -1280,6 +1281,77 @@
}
@Test
+ public void testDualVilteProviderStats() throws Exception {
+ // Pretend that network comes online.
+ expectDefaultSettings();
+ final int subId1 = 1;
+ final int subId2 = 2;
+ final NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{
+ buildImsState(IMSI_1, subId1, TEST_IFACE),
+ buildImsState(IMSI_2, subId2, TEST_IFACE2)};
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+
+ // Register custom provider and retrieve callback.
+ final TestableNetworkStatsProviderBinder provider =
+ new TestableNetworkStatsProviderBinder();
+ final INetworkStatsProviderCallback cb =
+ mService.registerNetworkStatsProvider("TEST", provider);
+ assertNotNull(cb);
+
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
+
+ // Verifies that one requestStatsUpdate will be called during iface update.
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
+
+ // Create some initial traffic and report to the service.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ final String vtIface1 = NetworkStats.IFACE_VT + subId1;
+ final String vtIface2 = NetworkStats.IFACE_VT + subId2;
+ final NetworkStats expectedStats = new NetworkStats(0L, 1)
+ .addEntry(new NetworkStats.Entry(vtIface1, UID_RED, SET_DEFAULT,
+ TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+ 128L, 2L, 128L, 2L, 1L))
+ .addEntry(new NetworkStats.Entry(vtIface2, UID_RED, SET_DEFAULT,
+ TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+ 64L, 1L, 64L, 1L, 1L));
+ cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats);
+
+ // Make another empty mutable stats object. This is necessary since the new NetworkStats
+ // object will be used to compare with the old one in NetworkStatsRecoder, two of them
+ // cannot be the same object.
+ expectNetworkStatsUidDetail(buildEmptyStats());
+
+ forcePollAndWaitForIdle();
+
+ // Verifies that one requestStatsUpdate and setAlert will be called during polling.
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
+ provider.expectOnSetAlert(MB_IN_BYTES);
+
+ // Verifies that service recorded history, does not verify uid tag part.
+ assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 1);
+
+ // Verifies that onStatsUpdated updates the stats accordingly.
+ NetworkStats stats = mSession.getSummaryForAllUid(
+ sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
+ assertEquals(1, stats.size());
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L);
+
+ stats = mSession.getSummaryForAllUid(
+ sTemplateImsi2, Long.MIN_VALUE, Long.MAX_VALUE, true);
+ assertEquals(1, stats.size());
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L);
+
+ // Verifies that unregister the callback will remove the provider from service.
+ cb.unregister();
+ forcePollAndWaitForIdle();
+ provider.assertNoCallback();
+ }
+
+ @Test
public void testStatsProviderSetAlert() throws Exception {
// Pretend that network comes online.
expectDefaultSettings();
@@ -1616,6 +1688,20 @@
TYPE_MOBILE);
}
+ private static NetworkStateSnapshot buildImsState(
+ String subscriberId, int subId, String ifaceName) {
+ final LinkProperties prop = new LinkProperties();
+ prop.setInterfaceName(ifaceName);
+ final NetworkCapabilities capabilities = new NetworkCapabilities();
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, true);
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_IMS, true);
+ capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ capabilities.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+ return new NetworkStateSnapshot(
+ MOBILE_NETWORK, capabilities, prop, subscriberId, TYPE_MOBILE);
+ }
+
private long getElapsedRealtime() {
return mElapsedRealtime;
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index babea36..f15d420 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -49,7 +49,9 @@
import android.annotation.NonNull;
import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
@@ -336,6 +338,13 @@
return captor.getValue();
}
+ private BroadcastReceiver getPackageChangeReceiver() {
+ final ArgumentCaptor<BroadcastReceiver> captor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mMockContext).registerReceiver(captor.capture(), any(), any(), any());
+ return captor.getValue();
+ }
+
private Vcn startAndGetVcnInstance(ParcelUuid uuid) {
mVcnMgmtSvc.setVcnConfig(uuid, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
return mVcnMgmtSvc.getAllVcns().get(uuid);
@@ -412,6 +421,42 @@
}
@Test
+ public void testPackageChangeListenerRegistered() throws Exception {
+ verify(mMockContext).registerReceiver(any(BroadcastReceiver.class), argThat(filter -> {
+ return filter.hasAction(Intent.ACTION_PACKAGE_ADDED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_REPLACED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
+ }), any(), any());
+ }
+
+ @Test
+ public void testPackageChangeListener_packageAdded() throws Exception {
+ final BroadcastReceiver receiver = getPackageChangeReceiver();
+
+ verify(mMockContext).registerReceiver(any(), argThat(filter -> {
+ return filter.hasAction(Intent.ACTION_PACKAGE_ADDED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_REPLACED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
+ }), any(), any());
+
+ receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_ADDED));
+ verify(mSubscriptionTracker).handleSubscriptionsChanged();
+ }
+
+ @Test
+ public void testPackageChangeListener_packageRemoved() throws Exception {
+ final BroadcastReceiver receiver = getPackageChangeReceiver();
+
+ verify(mMockContext).registerReceiver(any(), argThat(filter -> {
+ return filter.hasAction(Intent.ACTION_PACKAGE_REMOVED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
+ }), any(), any());
+
+ receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_REMOVED));
+ verify(mSubscriptionTracker).handleSubscriptionsChanged();
+ }
+
+ @Test
public void testSetVcnConfigRequiresNonSystemServer() throws Exception {
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 82da249..351e420 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -256,53 +256,35 @@
void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& options,
Printer* printer) {
- for (const auto& package : table.packages) {
- ValueHeadlinePrinter headline_printer(package->name, printer);
- ValueBodyPrinter body_printer(package->name, printer);
+ const auto table_view = table.GetPartitionedView();
+ for (const auto& package : table_view.packages) {
+ ValueHeadlinePrinter headline_printer(package.name, printer);
+ ValueBodyPrinter body_printer(package.name, printer);
printer->Print("Package name=");
- printer->Print(package->name);
- if (package->id) {
- printer->Print(StringPrintf(" id=%02x", package->id.value()));
+ printer->Print(package.name);
+ if (package.id) {
+ printer->Print(StringPrintf(" id=%02x", package.id.value()));
}
printer->Println();
printer->Indent();
- for (const auto& type : package->types) {
+ for (const auto& type : package.types) {
printer->Print("type ");
- printer->Print(to_string(type->type));
- if (type->id) {
- printer->Print(StringPrintf(" id=%02x", type->id.value()));
+ printer->Print(to_string(type.type));
+ if (type.id) {
+ printer->Print(StringPrintf(" id=%02x", type.id.value()));
}
- printer->Println(StringPrintf(" entryCount=%zd", type->entries.size()));
-
- std::vector<const ResourceEntry*> sorted_entries;
- for (const auto& entry : type->entries) {
- auto iter = std::lower_bound(
- sorted_entries.begin(), sorted_entries.end(), entry.get(),
- [](const ResourceEntry* a, const ResourceEntry* b) -> bool {
- if (a->id && b->id) {
- return a->id.value() < b->id.value();
- } else if (a->id) {
- return true;
- } else {
- return false;
- }
- });
- sorted_entries.insert(iter, entry.get());
- }
+ printer->Println(StringPrintf(" entryCount=%zd", type.entries.size()));
printer->Indent();
- for (const ResourceEntry* entry : sorted_entries) {
- const ResourceId id(package->id.value_or_default(0), type->id.value_or_default(0),
- entry->id.value_or_default(0));
-
+ for (const ResourceEntry* entry : type.entries) {
printer->Print("resource ");
- printer->Print(id.to_string());
+ printer->Print(entry->id.value_or_default(0).to_string());
printer->Print(" ");
// Write the name without the package (this is obvious and too verbose).
- printer->Print(to_string(type->type));
+ printer->Print(to_string(type.type));
printer->Print("/");
printer->Print(entry->name);
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index e930b47..830bc5f 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -113,7 +113,7 @@
}
std::string error;
- table = util::make_unique<ResourceTable>(/** validate_resources **/ false);
+ table = util::make_unique<ResourceTable>(ResourceTable::Validation::kDisabled);
if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) {
diag->Error(DiagMessage(source)
<< "failed to deserialize " << kProtoResourceTablePath << ": " << error);
@@ -157,7 +157,7 @@
io::IFile* table_file = collection->FindFile(kApkResourceTablePath);
if (table_file != nullptr) {
- table = util::make_unique<ResourceTable>(/** validate_resources **/ false);
+ table = util::make_unique<ResourceTable>(ResourceTable::Validation::kDisabled);
std::unique_ptr<io::IData> data = table_file->OpenAsData();
if (data == nullptr) {
diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath);
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 8fe0eb3..cf93870 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -138,7 +138,7 @@
uint32_t id;
ResourceId();
- ResourceId(const ResourceId& rhs);
+ ResourceId(const ResourceId& rhs) = default;
ResourceId(uint32_t res_id); // NOLINT(google-explicit-constructor)
ResourceId(uint8_t p, uint8_t t, uint16_t e);
@@ -222,8 +222,6 @@
inline ResourceId::ResourceId() : id(0) {}
-inline ResourceId::ResourceId(const ResourceId& rhs) : id(rhs.id) {}
-
inline ResourceId::ResourceId(uint32_t res_id) : id(res_id) {}
inline ResourceId::ResourceId(uint8_t p, uint8_t t, uint16_t e)
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 3d9be59..24c60b7 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -42,6 +42,10 @@
using android::idmap2::policy::kPolicyStringToFlag;
namespace aapt {
+namespace {
+constexpr const char* kPublicGroupTag = "public-group";
+constexpr const char* kStagingPublicGroupTag = "staging-public-group";
+} // namespace
constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
@@ -102,6 +106,7 @@
ResourceId id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
+ bool staged_api = false;
bool allow_new = false;
Maybe<OverlayableItem> overlayable_item;
@@ -118,43 +123,44 @@
res->comment = trimmed_comment.to_string();
}
+ NewResourceBuilder res_builder(res->name);
if (res->visibility_level != Visibility::Level::kUndefined) {
Visibility visibility;
visibility.level = res->visibility_level;
+ visibility.staged_api = res->staged_api;
visibility.source = res->source;
visibility.comment = res->comment;
- if (!table->SetVisibilityWithId(res->name, visibility, res->id, diag)) {
- return false;
- }
+ res_builder.SetVisibility(visibility);
+ }
+
+ if (res->id.is_valid()) {
+ res_builder.SetId(res->id);
}
if (res->allow_new) {
AllowNew allow_new;
allow_new.source = res->source;
allow_new.comment = res->comment;
- if (!table->SetAllowNew(res->name, allow_new, diag)) {
- return false;
- }
+ res_builder.SetAllowNew(allow_new);
}
if (res->overlayable_item) {
- if (!table->SetOverlayable(res->name, res->overlayable_item.value(), diag)) {
- return false;
- }
+ res_builder.SetOverlayable(res->overlayable_item.value());
}
if (res->value != nullptr) {
// Attach the comment, source and config to the value.
res->value->SetComment(std::move(res->comment));
res->value->SetSource(std::move(res->source));
-
- if (!table->AddResourceWithId(res->name, res->id, res->config, res->product,
- std::move(res->value), diag)) {
- return false;
- }
+ res_builder.SetValue(std::move(res->value), res->config, res->product);
}
bool error = false;
+ if (!res->name.entry.empty()) {
+ if (!table->AddResource(res_builder.Build(), diag)) {
+ return false;
+ }
+ }
for (ParsedResource& child : res->child_resources) {
error |= !AddResourcesToTable(table, diag, &child);
}
@@ -525,6 +531,7 @@
{"plurals", std::mem_fn(&ResourceParser::ParsePlural)},
{"public", std::mem_fn(&ResourceParser::ParsePublic)},
{"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
+ {"staging-public-group", std::mem_fn(&ResourceParser::ParseStagingPublicGroup)},
{"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
{"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle,
std::placeholders::_2, std::placeholders::_3)},
@@ -653,7 +660,8 @@
const auto bag_iter = elToBagMap.find(resource_type);
if (bag_iter != elToBagMap.end()) {
// Ensure we have a name (unless this is a <public-group> or <overlayable>).
- if (resource_type != "public-group" && resource_type != "overlayable") {
+ if (resource_type != kPublicGroupTag && resource_type != kStagingPublicGroupTag &&
+ resource_type != "overlayable") {
if (!maybe_name) {
diag_->Error(DiagMessage(out_resource->source)
<< "<" << parser->element_name() << "> missing 'name' attribute");
@@ -751,7 +759,7 @@
// table.
std::unique_ptr<Id> id = util::make_unique<Id>();
id->SetSource(source_.WithLine(begin_xml_line));
- table_->AddResource(name, {}, {}, std::move(id), diag_);
+ table_->AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), diag_);
};
// Process the raw value.
@@ -890,54 +898,45 @@
return true;
}
-bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
- if (options_.visibility) {
- diag_->Error(DiagMessage(out_resource->source)
- << "<public-group> tag not allowed with --visibility flag");
- return false;
- }
-
+template <typename Func>
+bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
+ const char* tag_name, IDiagnostics* diag, Func&& func) {
if (out_resource->config != ConfigDescription::DefaultConfig()) {
- diag_->Warn(DiagMessage(out_resource->source)
- << "ignoring configuration '" << out_resource->config
- << "' for <public-group> tag");
+ diag->Warn(DiagMessage(out_resource->source)
+ << "ignoring configuration '" << out_resource->config << "' for <" << tag_name
+ << "> tag");
}
Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
- diag_->Error(DiagMessage(out_resource->source)
- << "<public-group> must have a 'type' attribute");
+ diag->Error(DiagMessage(out_resource->source)
+ << "<" << tag_name << "> must have a 'type' attribute");
return false;
}
const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
if (!parsed_type) {
- diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
- << maybe_type.value()
- << "' in <public-group>");
+ diag->Error(DiagMessage(out_resource->source)
+ << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">");
return false;
}
- Maybe<StringPiece> maybe_id_str =
- xml::FindNonEmptyAttribute(parser, "first-id");
+ Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
if (!maybe_id_str) {
- diag_->Error(DiagMessage(out_resource->source)
- << "<public-group> must have a 'first-id' attribute");
+ diag->Error(DiagMessage(out_resource->source)
+ << "<" << tag_name << "> must have a 'first-id' attribute");
return false;
}
- Maybe<ResourceId> maybe_id =
- ResourceUtils::ParseResourceId(maybe_id_str.value());
+ Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
if (!maybe_id) {
- diag_->Error(DiagMessage(out_resource->source) << "invalid resource ID '"
- << maybe_id_str.value()
- << "' in <public-group>");
+ diag->Error(DiagMessage(out_resource->source)
+ << "invalid resource ID '" << maybe_id_str.value() << "' in <" << tag_name << ">");
return false;
}
- ResourceId next_id = maybe_id.value();
-
std::string comment;
+ ResourceId next_id = maybe_id.value();
bool error = false;
const size_t depth = parser->depth();
while (xml::XmlPullParser::NextChildNode(parser, depth)) {
@@ -949,53 +948,72 @@
continue;
}
- const Source item_source = source_.WithLine(parser->line_number());
+ const Source item_source = out_resource->source.WithLine(parser->line_number());
const std::string& element_namespace = parser->element_namespace();
const std::string& element_name = parser->element_name();
if (element_namespace.empty() && element_name == "public") {
- Maybe<StringPiece> maybe_name =
- xml::FindNonEmptyAttribute(parser, "name");
+ auto maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (!maybe_name) {
- diag_->Error(DiagMessage(item_source)
- << "<public> must have a 'name' attribute");
+ diag->Error(DiagMessage(item_source) << "<public> must have a 'name' attribute");
error = true;
continue;
}
if (xml::FindNonEmptyAttribute(parser, "id")) {
- diag_->Error(DiagMessage(item_source)
- << "'id' is ignored within <public-group>");
+ diag->Error(DiagMessage(item_source) << "'id' is ignored within <" << tag_name << ">");
error = true;
continue;
}
if (xml::FindNonEmptyAttribute(parser, "type")) {
- diag_->Error(DiagMessage(item_source)
- << "'type' is ignored within <public-group>");
+ diag->Error(DiagMessage(item_source) << "'type' is ignored within <" << tag_name << ">");
error = true;
continue;
}
- ParsedResource child_resource;
- child_resource.name.type = *parsed_type;
- child_resource.name.entry = maybe_name.value().to_string();
- child_resource.id = next_id;
- // NOLINTNEXTLINE(bugprone-use-after-move) move+reset comment
- child_resource.comment = std::move(comment);
- child_resource.source = item_source;
- child_resource.visibility_level = Visibility::Level::kPublic;
- out_resource->child_resources.push_back(std::move(child_resource));
+ ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{
+ .name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()},
+ .source = item_source,
+ .id = next_id,
+ .comment = std::move(comment),
+ });
- next_id.id += 1;
+ // Execute group specific code.
+ func(entry_res, next_id);
+ next_id.id++;
} else if (!ShouldIgnoreElement(element_namespace, element_name)) {
- diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
+ diag->Error(DiagMessage(item_source) << ":" << element_name << ">");
error = true;
}
}
return !error;
}
+bool ResourceParser::ParseStagingPublicGroup(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ return ParseGroupImpl(parser, out_resource, kStagingPublicGroupTag, diag_,
+ [](ParsedResource& parsed_entry, ResourceId id) {
+ parsed_entry.id = id;
+ parsed_entry.staged_api = true;
+ parsed_entry.visibility_level = Visibility::Level::kPublic;
+ });
+}
+
+bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+ if (options_.visibility) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << kPublicGroupTag << "> tag not allowed with --visibility flag");
+ return false;
+ }
+
+ return ParseGroupImpl(parser, out_resource, kPublicGroupTag, diag_,
+ [](ParsedResource& parsed_entry, ResourceId id) {
+ parsed_entry.id = id;
+ parsed_entry.visibility_level = Visibility::Level::kPublic;
+ });
+}
+
bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 9d3ecc8..af0db8c 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -99,6 +99,7 @@
bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 9b70079..4a509be 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -831,25 +831,38 @@
Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
ASSERT_TRUE(result);
-
- ASSERT_TRUE(result.value().package->id);
- ASSERT_TRUE(result.value().type->id);
ASSERT_TRUE(result.value().entry->id);
- ResourceId actual_id(result.value().package->id.value(),
- result.value().type->id.value(),
- result.value().entry->id.value());
- EXPECT_THAT(actual_id, Eq(ResourceId(0x01010040)));
+ EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01010040)));
+
+ result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.value().entry->id);
+ EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01010041)));
+}
+
+TEST_F(ResourceParserTest, StagingPublicGroup) {
+ std::string input = R"(
+ <staging-public-group type="attr" first-id="0x01ff0049">
+ <public name="foo" />
+ <public name="bar" />
+ </staging-public-group>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+ ASSERT_TRUE(result);
+
+ ASSERT_TRUE(result.value().entry->id);
+ EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01ff0049)));
+ EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_TRUE(result.value().entry->visibility.staged_api);
result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
ASSERT_TRUE(result);
- ASSERT_TRUE(result.value().package->id);
- ASSERT_TRUE(result.value().type->id);
ASSERT_TRUE(result.value().entry->id);
- actual_id = ResourceId(result.value().package->id.value(),
- result.value().type->id.value(),
- result.value().entry->id.value());
- EXPECT_THAT(actual_id, Eq(ResourceId(0x01010041)));
+ EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01ff004a)));
+ EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_TRUE(result.value().entry->visibility.staged_api);
}
TEST_F(ResourceParserTest, StrongestSymbolVisibilityWins) {
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index e0a9a31e..27f7bdd 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -18,20 +18,18 @@
#include <algorithm>
#include <memory>
-#include <string>
#include <tuple>
#include "android-base/logging.h"
-#include "android-base/stringprintf.h"
#include "androidfw/ConfigDescription.h"
#include "androidfw/ResourceTypes.h"
-#include "Debug.h"
#include "NameMangler.h"
+#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "trace/TraceBuffer.h"
#include "text/Unicode.h"
+#include "trace/TraceBuffer.h"
#include "util/Util.h"
using ::aapt::text::IsValidResourceEntryName;
@@ -43,135 +41,97 @@
const char* Overlayable::kActorScheme = "overlay";
-static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
- const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
- return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
+namespace {
+bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
+ return lhs->type < rhs;
}
template <typename T>
-static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
+bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
}
template <typename T>
-static bool less_than_struct_with_name_and_id(const std::unique_ptr<T>& lhs,
- const std::pair<StringPiece, Maybe<uint16_t>>& rhs) {
- int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size());
- return name_cmp < 0 || (name_cmp == 0 && rhs.second && lhs->id < rhs.second);
+bool greater_than_struct_with_name(const StringPiece& lhs, const std::unique_ptr<T>& rhs) {
+ return rhs->name.compare(0, rhs->name.size(), lhs.data(), lhs.size()) > 0;
}
-ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
- const auto last = packages.end();
- auto iter = std::lower_bound(packages.begin(), last, name,
- less_than_struct_with_name<ResourceTablePackage>);
- if (iter != last && name == (*iter)->name) {
- return iter->get();
+template <typename T>
+struct NameEqualRange {
+ bool operator()(const std::unique_ptr<T>& lhs, const StringPiece& rhs) const {
+ return less_than_struct_with_name<T>(lhs, rhs);
}
- return nullptr;
+ bool operator()(const StringPiece& lhs, const std::unique_ptr<T>& rhs) const {
+ return greater_than_struct_with_name<T>(lhs, rhs);
+ }
+};
+
+template <typename T, typename U>
+bool less_than_struct_with_name_and_id(const T& lhs,
+ const std::pair<std::string_view, Maybe<U>>& rhs) {
+ if (lhs.id != rhs.second) {
+ return lhs.id < rhs.second;
+ }
+ return lhs.name.compare(0, lhs.name.size(), rhs.first.data(), rhs.first.size()) < 0;
}
-ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
- for (auto& package : packages) {
- if (package->id && package->id.value() == id) {
- return package.get();
- }
- }
- return nullptr;
+template <typename T, typename Func, typename Elements>
+T* FindElementsRunAction(const android::StringPiece& name, Elements& entries, Func action) {
+ const auto iter =
+ std::lower_bound(entries.begin(), entries.end(), name, less_than_struct_with_name<T>);
+ const bool found = iter != entries.end() && name == (*iter)->name;
+ return action(found, iter);
}
-ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
- TRACE_CALL();
- ResourceTablePackage* package = FindOrCreatePackage(name);
- if (id && !package->id) {
- package->id = id;
- return package;
- }
+} // namespace
- if (id && package->id && package->id.value() != id.value()) {
- return nullptr;
- }
- return package;
+ResourceTable::ResourceTable(ResourceTable::Validation validation) : validation_(validation) {
}
-ResourceTablePackage* ResourceTable::CreatePackageAllowingDuplicateNames(const StringPiece& name,
- const Maybe<uint8_t> id) {
- const auto last = packages.end();
- auto iter = std::lower_bound(packages.begin(), last, std::make_pair(name, id),
- less_than_struct_with_name_and_id<ResourceTablePackage>);
-
- if (iter != last && name == (*iter)->name && id == (*iter)->id) {
- return iter->get();
- }
-
- std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
- new_package->name = name.to_string();
- new_package->id = id;
- return packages.emplace(iter, std::move(new_package))->get();
+ResourceTablePackage* ResourceTable::FindPackage(const android::StringPiece& name) const {
+ return FindElementsRunAction<ResourceTablePackage>(
+ name, packages, [&](bool found, auto& iter) { return found ? iter->get() : nullptr; });
}
-ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
- const auto last = packages.end();
- auto iter = std::lower_bound(packages.begin(), last, name,
- less_than_struct_with_name<ResourceTablePackage>);
- if (iter != last && name == (*iter)->name) {
- return iter->get();
- }
-
- std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
- new_package->name = name.to_string();
- return packages.emplace(iter, std::move(new_package))->get();
+ResourceTablePackage* ResourceTable::FindOrCreatePackage(const android::StringPiece& name) {
+ return FindElementsRunAction<ResourceTablePackage>(name, packages, [&](bool found, auto& iter) {
+ return found ? iter->get() : packages.emplace(iter, new ResourceTablePackage(name))->get();
+ });
}
-ResourceTableType* ResourceTablePackage::FindType(ResourceType type, const Maybe<uint8_t> id) {
- const auto last = types.end();
- auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
- less_than_type_and_id);
- if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
- return iter->get();
- }
- return nullptr;
+template <typename Func, typename Elements>
+static ResourceTableType* FindTypeRunAction(ResourceType type, Elements& entries, Func action) {
+ const auto iter = std::lower_bound(entries.begin(), entries.end(), type, less_than_type);
+ const bool found = iter != entries.end() && type == (*iter)->type;
+ return action(found, iter);
}
-ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type,
- const Maybe<uint8_t> id) {
- const auto last = types.end();
- auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
- less_than_type_and_id);
- if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
- return iter->get();
- }
-
- auto new_type = new ResourceTableType(type);
- new_type->id = id;
- return types.emplace(iter, std::move(new_type))->get();
+ResourceTableType* ResourceTablePackage::FindType(ResourceType type) const {
+ return FindTypeRunAction(type, types,
+ [&](bool found, auto& iter) { return found ? iter->get() : nullptr; });
}
-ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name, const Maybe<uint16_t> id) {
- const auto last = entries.end();
- auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
- less_than_struct_with_name_and_id<ResourceEntry>);
- if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
- return iter->get();
- }
- return nullptr;
+ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) {
+ return FindTypeRunAction(type, types, [&](bool found, auto& iter) {
+ return found ? iter->get() : types.emplace(iter, new ResourceTableType(type))->get();
+ });
}
-ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name,
- const Maybe<uint16_t > id) {
- auto last = entries.end();
- auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
- less_than_struct_with_name_and_id<ResourceEntry>);
- if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
- return iter->get();
- }
-
- auto new_entry = new ResourceEntry(name);
- new_entry->id = id;
- return entries.emplace(iter, std::move(new_entry))->get();
+ResourceEntry* ResourceTableType::CreateEntry(const android::StringPiece& name) {
+ return FindElementsRunAction<ResourceEntry>(name, entries, [&](bool found, auto& iter) {
+ return entries.emplace(iter, new ResourceEntry(name))->get();
+ });
}
-ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
- return FindValue(config, StringPiece());
+ResourceEntry* ResourceTableType::FindEntry(const android::StringPiece& name) const {
+ return FindElementsRunAction<ResourceEntry>(
+ name, entries, [&](bool found, auto& iter) { return found ? iter->get() : nullptr; });
+}
+
+ResourceEntry* ResourceTableType::FindOrCreateEntry(const android::StringPiece& name) {
+ return FindElementsRunAction<ResourceEntry>(name, entries, [&](bool found, auto& iter) {
+ return found ? iter->get() : entries.emplace(iter, new ResourceEntry(name))->get();
+ });
}
struct ConfigKey {
@@ -188,7 +148,20 @@
}
ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
- const StringPiece& product) {
+ android::StringPiece product) {
+ auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
+ lt_config_key_ref);
+ if (iter != values.end()) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
+ }
+ }
+ return nullptr;
+}
+
+const ResourceConfigValue* ResourceEntry::FindValue(const android::ConfigDescription& config,
+ android::StringPiece product) const {
auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
lt_config_key_ref);
if (iter != values.end()) {
@@ -323,303 +296,242 @@
return CollisionResult::kConflict;
}
-ResourceTable::CollisionResult ResourceTable::IgnoreCollision(Value* /** existing **/,
- Value* /** incoming **/) {
- return CollisionResult::kKeepBoth;
-}
-
-static StringPiece ResourceNameValidator(const StringPiece& name) {
- if (!IsValidResourceEntryName(name)) {
- return name;
- }
- return {};
-}
-
-static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
- return {};
-}
-
-bool ResourceTable::AddResource(const ResourceNameRef& name,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag) {
- return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
- (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
- (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
-}
-
-bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
- const ConfigDescription& config, const StringPiece& product,
- std::unique_ptr<Value> value, IDiagnostics* diag) {
- return AddResourceImpl(name, res_id, config, product, std::move(value),
- (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
- (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
-}
-
-bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
- const StringPiece& product, std::unique_ptr<Value> value,
- IDiagnostics* diag) {
- return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
- (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
-}
-
-bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value, IDiagnostics* diag) {
- return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
- (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
-}
-
-bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
- const Source& source, IDiagnostics* diag) {
- const StringPiece bad_char = name_validator(name.entry);
- if (!bad_char.empty()) {
- diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
- << name.entry << "'. Invalid character '" << bad_char << "'");
- return false;
- }
- return true;
-}
-
-bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
- const ConfigDescription& config, const StringPiece& product,
- std::unique_ptr<Value> value, NameValidator name_validator,
- const CollisionResolverFunc& conflict_resolver,
- IDiagnostics* diag) {
- CHECK(value != nullptr);
- CHECK(diag != nullptr);
-
- const Source& source = value->GetSource();
- if (!ValidateName(name_validator, name, source, diag)) {
- return false;
+template <typename T, typename Comparer>
+struct SortedVectorInserter : public Comparer {
+ std::pair<bool, typename std::vector<T>::iterator> LowerBound(std::vector<T>& el,
+ const T& value) {
+ auto it = std::lower_bound(el.begin(), el.end(), value, [&](auto& lhs, auto& rhs) {
+ return Comparer::operator()(lhs, rhs);
+ });
+ bool found =
+ it != el.end() && !Comparer::operator()(*it, value) && !Comparer::operator()(value, *it);
+ return std::make_pair(found, it);
}
- // Check for package names appearing twice with two different package ids
- ResourceTablePackage* package = FindOrCreatePackage(name.package);
- if (res_id.is_valid() && package->id && package->id.value() != res_id.package_id()) {
- diag->Error(DiagMessage(source)
- << "trying to add resource '" << name << "' with ID " << res_id
- << " but package '" << package->name << "' already has ID "
- << StringPrintf("%02x", package->id.value()));
- return false;
+ T* Insert(std::vector<T>& el, T&& value) {
+ auto [found, it] = LowerBound(el, value);
+ if (found) {
+ return &*it;
+ }
+ return &*el.insert(it, std::move(value));
}
+};
- // Whether or not to error on duplicate resources
- bool check_id = validate_resources_ && res_id.is_valid();
- // Whether or not to create a duplicate resource if the id does not match
- bool use_id = !validate_resources_ && res_id.is_valid();
-
- ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
- : Maybe<uint8_t>());
-
- // Check for types appearing twice with two different type ids
- if (check_id && type->id && type->id.value() != res_id.type_id()) {
- diag->Error(DiagMessage(source)
- << "trying to add resource '" << name << "' with ID " << res_id
- << " but type '" << type->type << "' already has ID "
- << StringPrintf("%02x", type->id.value()));
- return false;
+struct PackageViewComparer {
+ bool operator()(const ResourceTablePackageView& lhs, const ResourceTablePackageView& rhs) {
+ return less_than_struct_with_name_and_id<ResourceTablePackageView, uint8_t>(
+ lhs, std::make_pair(rhs.name, rhs.id));
}
+};
- ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
- : Maybe<uint16_t>());
-
- // Check for entries appearing twice with two different entry ids
- if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
- diag->Error(DiagMessage(source)
- << "trying to add resource '" << name << "' with ID " << res_id
- << " but resource already has ID "
- << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
- return false;
+struct TypeViewComparer {
+ bool operator()(const ResourceTableTypeView& lhs, const ResourceTableTypeView& rhs) {
+ return lhs.id != rhs.id ? lhs.id < rhs.id : lhs.type < rhs.type;
}
+};
- ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
- if (!config_value->value) {
- // Resource does not exist, add it now.
- config_value->value = std::move(value);
- } else {
- switch (conflict_resolver(config_value->value.get(), value.get())) {
- case CollisionResult::kKeepBoth:
- // Insert the value ignoring for duplicate configurations
- entry->values.push_back(util::make_unique<ResourceConfigValue>(config, product));
- entry->values.back()->value = std::move(value);
- break;
+struct EntryViewComparer {
+ bool operator()(const ResourceEntry* lhs, const ResourceEntry* rhs) {
+ return less_than_struct_with_name_and_id<ResourceEntry, ResourceId>(
+ *lhs, std::make_pair(rhs->name, rhs->id));
+ }
+};
- case CollisionResult::kTakeNew:
- // Take the incoming value.
- config_value->value = std::move(value);
- break;
+ResourceTableView ResourceTable::GetPartitionedView() const {
+ ResourceTableView view;
+ SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
+ SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
+ SortedVectorInserter<const ResourceEntry*, EntryViewComparer> entry_inserter;
- case CollisionResult::kConflict:
- diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
- << "with config '" << config << "'");
- diag->Error(DiagMessage(source) << "resource previously defined here");
- return false;
+ for (const auto& package : packages) {
+ for (const auto& type : package->types) {
+ for (const auto& entry : type->entries) {
+ ResourceTablePackageView new_package{
+ package->name, entry->id ? entry->id.value().package_id() : Maybe<uint8_t>{}};
+ auto view_package = package_inserter.Insert(view.packages, std::move(new_package));
- case CollisionResult::kKeepOriginal:
- break;
+ ResourceTableTypeView new_type{type->type,
+ entry->id ? entry->id.value().type_id() : Maybe<uint8_t>{}};
+ auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
+
+ if (entry->visibility.level == Visibility::Level::kPublic) {
+ // Only mark the type visibility level as public, it doesn't care about being private.
+ view_type->visibility_level = Visibility::Level::kPublic;
+ }
+
+ entry_inserter.Insert(view_type->entries, entry.get());
+ }
}
}
- if (res_id.is_valid()) {
- package->id = res_id.package_id();
- type->id = res_id.type_id();
- entry->id = res_id.entry_id();
+ // The android runtime does not support querying resources when the there are multiple type ids
+ // for the same resource type within the same package. For this reason, if there are types with
+ // multiple type ids, each type needs to exist in its own package in order to be queried by name.
+ std::vector<ResourceTablePackageView> new_packages;
+ for (auto& package : view.packages) {
+ // If a new package was already created for a different type within this package, then
+ // we can reuse those packages for other types that need to be extracted from this package.
+ // `start_index` is the index of the first newly created package that can be reused.
+ const size_t start_index = new_packages.size();
+ std::map<ResourceType, size_t> type_new_package_index;
+ for (auto type_it = package.types.begin(); type_it != package.types.end();) {
+ auto& type = *type_it;
+ auto type_index_iter = type_new_package_index.find(type.type);
+ if (type_index_iter == type_new_package_index.end()) {
+ // First occurrence of the resource type in this package. Keep it in this package.
+ type_new_package_index.insert(type_index_iter, std::make_pair(type.type, start_index));
+ ++type_it;
+ continue;
+ }
+
+ // The resource type has already been seen for this package, so this type must be extracted to
+ // a new separate package.
+ const size_t index = type_index_iter->second;
+ if (new_packages.size() == index) {
+ new_packages.emplace_back(ResourceTablePackageView{package.name, package.id});
+ type_new_package_index[type.type] = index + 1;
+ }
+
+ // Move the type into a new package
+ auto& other_package = new_packages[index];
+ type_inserter.Insert(other_package.types, std::move(type));
+ type_it = package.types.erase(type_it);
+ }
}
- return true;
-}
-
-bool ResourceTable::GetValidateResources() {
- return validate_resources_;
-}
-
-bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
- IDiagnostics* diag) {
- return SetVisibilityImpl(name, visibility, {}, ResourceNameValidator, diag);
-}
-
-bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
- const ResourceId& res_id, IDiagnostics* diag) {
- return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
-}
-
-bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
- const Visibility& visibility,
- const ResourceId& res_id, IDiagnostics* diag) {
- return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
-}
-
-bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
- const ResourceId& res_id, NameValidator name_validator,
- IDiagnostics* diag) {
- CHECK(diag != nullptr);
-
- const Source& source = visibility.source;
- if (!ValidateName(name_validator, name, source, diag)) {
- return false;
+ for (auto& new_package : new_packages) {
+ // Insert newly created packages after their original packages
+ auto [_, it] = package_inserter.LowerBound(view.packages, new_package);
+ view.packages.insert(++it, std::move(new_package));
}
- // Check for package names appearing twice with two different package ids
- ResourceTablePackage* package = FindOrCreatePackage(name.package);
- if (res_id.is_valid() && package->id && package->id.value() != res_id.package_id()) {
+ return view;
+}
+
+bool ResourceTable::AddResource(NewResource&& res, IDiagnostics* diag) {
+ CHECK(diag != nullptr) << "Diagnostic pointer is null";
+
+ const bool validate = validation_ == Validation::kEnabled;
+ const Source source = res.value ? res.value->GetSource() : Source{};
+ if (validate && !res.allow_mangled && !IsValidResourceEntryName(res.name.entry)) {
diag->Error(DiagMessage(source)
- << "trying to add resource '" << name << "' with ID " << res_id
- << " but package '" << package->name << "' already has ID "
- << StringPrintf("%02x", package->id.value()));
+ << "resource '" << res.name << "' has invalid entry name '" << res.name.entry);
return false;
}
- // Whether or not to error on duplicate resources
- bool check_id = validate_resources_ && res_id.is_valid();
- // Whether or not to create a duplicate resource if the id does not match
- bool use_id = !validate_resources_ && res_id.is_valid();
-
- ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
- : Maybe<uint8_t>());
-
- // Check for types appearing twice with two different type ids
- if (check_id && type->id && type->id.value() != res_id.type_id()) {
- diag->Error(DiagMessage(source)
- << "trying to add resource '" << name << "' with ID " << res_id
- << " but type '" << type->type << "' already has ID "
- << StringPrintf("%02x", type->id.value()));
+ if (res.id.has_value() && !res.id->first.is_valid()) {
+ diag->Error(DiagMessage(source) << "trying to add resource '" << res.name << "' with ID "
+ << res.id->first << " but that ID is invalid");
return false;
}
- ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
- : Maybe<uint16_t>());
+ auto package = FindOrCreatePackage(res.name.package);
+ auto type = package->FindOrCreateType(res.name.type);
+ auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), res.name.entry,
+ NameEqualRange<ResourceEntry>{});
+ const size_t entry_count = std::distance(entry_it.first, entry_it.second);
- // Check for entries appearing twice with two different entry ids
- if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
- diag->Error(DiagMessage(source)
- << "trying to add resource '" << name << "' with ID " << res_id
- << " but resource already has ID "
- << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
- return false;
+ ResourceEntry* entry;
+ if (entry_count == 0) {
+ // Adding a new resource
+ entry = type->CreateEntry(res.name.entry);
+ } else if (entry_count == 1) {
+ // Assume that the existing resource is being modified
+ entry = entry_it.first->get();
+ } else {
+ // Multiple resources with the same name exist in the resource table. The only way to
+ // distinguish between them is using resource id since each resource should have a unique id.
+ CHECK(res.id.has_value()) << "ambiguous modification of resource entry '" << res.name
+ << "' without specifying a resource id.";
+ entry = entry_it.first->get();
+ for (auto it = entry_it.first; it != entry_it.second; ++it) {
+ CHECK((bool)(*it)->id) << "ambiguous modification of resource entry '" << res.name
+ << "' with multiple entries without resource ids";
+ if ((*it)->id == res.id->first) {
+ entry = it->get();
+ break;
+ }
+ }
}
- if (res_id.is_valid()) {
- package->id = res_id.package_id();
- type->id = res_id.type_id();
- entry->id = res_id.entry_id();
+ if (res.id.has_value()) {
+ if (entry->id && entry->id.value() != res.id->first) {
+ if (res.id->second != OnIdConflict::CREATE_ENTRY) {
+ diag->Error(DiagMessage(source)
+ << "trying to add resource '" << res.name << "' with ID " << res.id->first
+ << " but resource already has ID " << entry->id.value());
+ return false;
+ }
+ entry = type->CreateEntry(res.name.entry);
+ }
+ entry->id = res.id->first;
}
- // Only mark the type visibility level as public, it doesn't care about being private.
- if (visibility.level == Visibility::Level::kPublic) {
- type->visibility_level = Visibility::Level::kPublic;
+ if (res.visibility.has_value()) {
+ // Only mark the type visibility level as public, it doesn't care about being private.
+ if (res.visibility->level == Visibility::Level::kPublic) {
+ type->visibility_level = Visibility::Level::kPublic;
+ }
+
+ if (res.visibility->level > entry->visibility.level) {
+ // This symbol definition takes precedence, replace.
+ entry->visibility = res.visibility.value();
+ }
+
+ if (res.visibility->staged_api) {
+ entry->visibility.staged_api = entry->visibility.staged_api;
+ }
}
- if (visibility.level == Visibility::Level::kUndefined &&
- entry->visibility.level != Visibility::Level::kUndefined) {
- // We can't undefine a symbol (remove its visibility). Ignore.
- return true;
+ if (res.overlayable.has_value()) {
+ if (entry->overlayable_item) {
+ diag->Error(DiagMessage(res.overlayable->source)
+ << "duplicate overlayable declaration for resource '" << res.name << "'");
+ diag->Error(DiagMessage(entry->overlayable_item.value().source)
+ << "previous declaration here");
+ return false;
+ }
+ entry->overlayable_item = res.overlayable.value();
}
- if (visibility.level < entry->visibility.level) {
- // We can't downgrade public to private. Ignore.
- return true;
+ if (res.allow_new.has_value()) {
+ entry->allow_new = res.allow_new.value();
}
- // This symbol definition takes precedence, replace.
- entry->visibility = visibility;
- return true;
-}
+ if (res.value != nullptr) {
+ auto config_value = entry->FindOrCreateValue(res.config, res.product);
+ if (!config_value->value) {
+ // Resource does not exist, add it now.
+ config_value->value = std::move(res.value);
+ } else {
+ // When validation is enabled, ensure that a resource cannot have multiple values defined for
+ // the same configuration.
+ auto result = validate ? ResolveValueCollision(config_value->value.get(), res.value.get())
+ : CollisionResult::kKeepBoth;
+ switch (result) {
+ case CollisionResult::kKeepBoth:
+ // Insert the value ignoring for duplicate configurations
+ entry->values.push_back(util::make_unique<ResourceConfigValue>(res.config, res.product));
+ entry->values.back()->value = std::move(res.value);
+ break;
-bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
- IDiagnostics* diag) {
- return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
-}
+ case CollisionResult::kTakeNew:
+ // Take the incoming value.
+ config_value->value = std::move(res.value);
+ break;
-bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
- IDiagnostics* diag) {
- return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
-}
+ case CollisionResult::kConflict:
+ diag->Error(DiagMessage(source) << "duplicate value for resource '" << res.name << "' "
+ << "with config '" << res.config << "'");
+ diag->Error(DiagMessage(source) << "resource previously defined here");
+ return false;
-bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
- NameValidator name_validator, IDiagnostics* diag) {
- CHECK(diag != nullptr);
-
- if (!ValidateName(name_validator, name, allow_new.source, diag)) {
- return false;
+ case CollisionResult::kKeepOriginal:
+ break;
+ }
+ }
}
- ResourceTablePackage* package = FindOrCreatePackage(name.package);
- ResourceTableType* type = package->FindOrCreateType(name.type);
- ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
- entry->allow_new = allow_new;
- return true;
-}
-
-bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
- IDiagnostics* diag) {
- return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
-}
-
-bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
- const OverlayableItem& overlayable,
- NameValidator name_validator, IDiagnostics *diag) {
- CHECK(diag != nullptr);
-
- if (!ValidateName(name_validator, name, overlayable.source, diag)) {
- return false;
- }
-
- ResourceTablePackage* package = FindOrCreatePackage(name.package);
- ResourceTableType* type = package->FindOrCreateType(name.type);
- ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
-
- if (entry->overlayable_item) {
- diag->Error(DiagMessage(overlayable.source)
- << "duplicate overlayable declaration for resource '" << name << "'");
- diag->Error(DiagMessage(entry->overlayable_item.value().source)
- << "previous declaration here");
- return false;
- }
-
- entry->overlayable_item = overlayable;
return true;
}
@@ -641,17 +553,38 @@
return SearchResult{package, type, entry};
}
+Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name,
+ ResourceId id) const {
+ ResourceTablePackage* package = FindPackage(name.package);
+ if (package == nullptr) {
+ return {};
+ }
+
+ ResourceTableType* type = package->FindType(name.type);
+ if (type == nullptr) {
+ return {};
+ }
+
+ auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), name.entry,
+ NameEqualRange<ResourceEntry>{});
+ for (auto it = entry_it.first; it != entry_it.second; ++it) {
+ if ((*it)->id == id) {
+ return SearchResult{package, type, it->get()};
+ }
+ }
+ return {};
+}
+
std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
for (const auto& pkg : packages) {
- ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
+ ResourceTablePackage* new_pkg = new_table->FindOrCreatePackage(pkg->name);
for (const auto& type : pkg->types) {
ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
- new_type->id = type->id;
new_type->visibility_level = type->visibility_level;
for (const auto& entry : type->entries) {
- ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
+ ResourceEntry* new_entry = new_type->CreateEntry(entry->name);
new_entry->id = entry->id;
new_entry->visibility = entry->visibility;
new_entry->allow_new = entry->allow_new;
@@ -668,4 +601,51 @@
return new_table;
}
+NewResourceBuilder::NewResourceBuilder(const ResourceNameRef& name) {
+ res_.name = name.ToResourceName();
+}
+
+NewResourceBuilder::NewResourceBuilder(const std::string& name) {
+ ResourceNameRef ref;
+ CHECK(ResourceUtils::ParseResourceName(name, &ref)) << "invalid resource name: " << name;
+ res_.name = ref.ToResourceName();
+}
+
+NewResourceBuilder& NewResourceBuilder::SetValue(std::unique_ptr<Value> value,
+ android::ConfigDescription config,
+ std::string product) {
+ res_.value = std::move(value);
+ res_.config = std::move(config);
+ res_.product = std::move(product);
+ return *this;
+}
+
+NewResourceBuilder& NewResourceBuilder::SetId(ResourceId id, OnIdConflict on_conflict) {
+ res_.id = std::make_pair(id, on_conflict);
+ return *this;
+}
+
+NewResourceBuilder& NewResourceBuilder::SetVisibility(Visibility visibility) {
+ res_.visibility = std::move(visibility);
+ return *this;
+}
+
+NewResourceBuilder& NewResourceBuilder::SetOverlayable(OverlayableItem overlayable) {
+ res_.overlayable = std::move(overlayable);
+ return *this;
+}
+NewResourceBuilder& NewResourceBuilder::SetAllowNew(AllowNew allow_new) {
+ res_.allow_new = std::move(allow_new);
+ return *this;
+}
+
+NewResourceBuilder& NewResourceBuilder::SetAllowMangled(bool allow_mangled) {
+ res_.allow_mangled = allow_mangled;
+ return *this;
+}
+
+NewResource NewResourceBuilder::Build() {
+ return std::move(res_);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 93a7a31..080ecc2 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -51,6 +51,11 @@
Level level = Level::kUndefined;
Source source;
std::string comment;
+
+ // Indicates that the resource id may change across builds and that the public R.java identifier
+ // for this resource should not be final. This is set to `true` for resources in `staging-group`
+ // tags.
+ bool staged_api = false;
};
// Represents <add-resource> in an overlay.
@@ -109,7 +114,7 @@
const std::string name;
// The entry ID for this resource (the EEEE in 0xPPTTEEEE).
- Maybe<uint16_t> id;
+ Maybe<ResourceId> id;
// Whether this resource is public (and must maintain the same entry ID across builds).
Visibility visibility;
@@ -124,10 +129,10 @@
explicit ResourceEntry(const android::StringPiece& name) : name(name.to_string()) {}
- ResourceConfigValue* FindValue(const android::ConfigDescription& config);
-
ResourceConfigValue* FindValue(const android::ConfigDescription& config,
- const android::StringPiece& product);
+ android::StringPiece product = {});
+ const ResourceConfigValue* FindValue(const android::ConfigDescription& config,
+ android::StringPiece product = {}) const;
ResourceConfigValue* FindOrCreateValue(const android::ConfigDescription& config,
const android::StringPiece& product);
@@ -156,9 +161,6 @@
// The logical type of resource (string, drawable, layout, etc.).
const ResourceType type;
- // The type ID for this resource (the TT in 0xPPTTEEEE).
- Maybe<uint8_t> id;
-
// Whether this type is public (and must maintain the same type ID across builds).
Visibility::Level visibility_level = Visibility::Level::kUndefined;
@@ -167,10 +169,9 @@
explicit ResourceTableType(const ResourceType type) : type(type) {}
- ResourceEntry* FindEntry(const android::StringPiece& name,
- Maybe<uint16_t> id = Maybe<uint16_t>());
- ResourceEntry* FindOrCreateEntry(const android::StringPiece& name,
- Maybe<uint16_t> id = Maybe<uint16_t>());
+ ResourceEntry* CreateEntry(const android::StringPiece& name);
+ ResourceEntry* FindEntry(const android::StringPiece& name) const;
+ ResourceEntry* FindOrCreateEntry(const android::StringPiece& name);
private:
DISALLOW_COPY_AND_ASSIGN(ResourceTableType);
@@ -180,70 +181,99 @@
public:
std::string name;
- // The package ID (the PP in 0xPPTTEEEE).
- Maybe<uint8_t> id;
-
std::vector<std::unique_ptr<ResourceTableType>> types;
+ explicit ResourceTablePackage(const android::StringPiece& name) : name(name.to_string()) {
+ }
+
ResourceTablePackage() = default;
- ResourceTableType* FindType(ResourceType type, Maybe<uint8_t> id = Maybe<uint8_t>());
- ResourceTableType* FindOrCreateType(const ResourceType type,
- Maybe<uint8_t> id = Maybe<uint8_t>());
+ ResourceTableType* FindType(ResourceType type) const;
+ ResourceTableType* FindOrCreateType(ResourceType type);
private:
DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
};
+struct ResourceTableTypeView {
+ ResourceType type;
+ Maybe<uint8_t> id;
+ Visibility::Level visibility_level = Visibility::Level::kUndefined;
+
+ // Entries sorted in ascending entry id order. If ids have not been assigned, the entries are
+ // // sorted lexicographically.
+ std::vector<const ResourceEntry*> entries;
+};
+
+struct ResourceTablePackageView {
+ std::string name;
+ Maybe<uint8_t> id;
+ // Types sorted in ascending type id order. If ids have not been assigned, the types are sorted by
+ // their declaration order in the ResourceType enum.
+ std::vector<ResourceTableTypeView> types;
+};
+
+struct ResourceTableView {
+ // Packages sorted in ascending package id order. If ids have not been assigned, the packages are
+ // sorted lexicographically.
+ std::vector<ResourceTablePackageView> packages;
+};
+
+enum class OnIdConflict {
+ // If the resource entry already exists but has a different resource id, the resource value will
+ // not be added to the table.
+ ERROR,
+
+ // If the resource entry already exists but has a different resource id, create a new resource
+ // with this resource name and id combination.
+ CREATE_ENTRY,
+};
+
+struct NewResource {
+ ResourceName name;
+ std::unique_ptr<Value> value;
+ android::ConfigDescription config;
+ std::string product;
+ std::optional<std::pair<ResourceId, OnIdConflict>> id;
+ std::optional<Visibility> visibility;
+ std::optional<OverlayableItem> overlayable;
+ std::optional<AllowNew> allow_new;
+ bool allow_mangled = false;
+};
+
+struct NewResourceBuilder {
+ explicit NewResourceBuilder(const ResourceNameRef& name);
+ explicit NewResourceBuilder(const std::string& name);
+ NewResourceBuilder& SetValue(std::unique_ptr<Value> value, android::ConfigDescription config = {},
+ std::string product = {});
+ NewResourceBuilder& SetId(ResourceId id, OnIdConflict on_conflict = OnIdConflict::ERROR);
+ NewResourceBuilder& SetVisibility(Visibility id);
+ NewResourceBuilder& SetOverlayable(OverlayableItem overlayable);
+ NewResourceBuilder& SetAllowNew(AllowNew allow_new);
+ NewResourceBuilder& SetAllowMangled(bool allow_mangled);
+ NewResource Build();
+
+ private:
+ NewResource res_;
+};
+
// The container and index for all resources defined for an app.
class ResourceTable {
public:
- ResourceTable() = default;
- explicit ResourceTable(bool validate_resources) : validate_resources_(validate_resources) {}
+ enum class Validation {
+ kEnabled,
+ kDisabled,
+ };
enum class CollisionResult { kKeepBoth, kKeepOriginal, kConflict, kTakeNew };
- using CollisionResolverFunc = std::function<CollisionResult(Value*, Value*)>;
+ ResourceTable() = default;
+ explicit ResourceTable(Validation validation);
- // When a collision of resources occurs, this method decides which value to keep.
- static CollisionResult ResolveValueCollision(Value* existing, Value* incoming);
+ bool AddResource(NewResource&& res, IDiagnostics* diag);
- // When a collision of resources occurs, this method keeps both values
- static CollisionResult IgnoreCollision(Value* existing, Value* incoming);
-
- bool AddResource(const ResourceNameRef& name, const android::ConfigDescription& config,
- const android::StringPiece& product, std::unique_ptr<Value> value,
- IDiagnostics* diag);
-
- bool AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
- const android::ConfigDescription& config,
- const android::StringPiece& product, std::unique_ptr<Value> value,
- IDiagnostics* diag);
-
- // Same as AddResource, but doesn't verify the validity of the name. This is used
- // when loading resources from an existing binary resource table that may have mangled names.
- bool AddResourceMangled(const ResourceNameRef& name, const android::ConfigDescription& config,
- const android::StringPiece& product, std::unique_ptr<Value> value,
- IDiagnostics* diag);
-
- bool AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
- const android::ConfigDescription& config,
- const android::StringPiece& product, std::unique_ptr<Value> value,
- IDiagnostics* diag);
-
- bool GetValidateResources();
-
- bool SetVisibility(const ResourceNameRef& name, const Visibility& visibility, IDiagnostics* diag);
- bool SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
- const ResourceId& res_id, IDiagnostics* diag);
- bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
- const ResourceId& res_id, IDiagnostics* diag);
-
- bool SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
- IDiagnostics *diag);
-
- bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
- bool SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
- IDiagnostics* diag);
+ // Retrieves a sorted a view of the packages, types, and entries sorted in ascending resource id
+ // order.
+ ResourceTableView GetPartitionedView() const;
struct SearchResult {
ResourceTablePackage* package;
@@ -252,23 +282,19 @@
};
Maybe<SearchResult> FindResource(const ResourceNameRef& name) const;
+ Maybe<SearchResult> FindResource(const ResourceNameRef& name, ResourceId id) const;
// Returns the package struct with the given name, or nullptr if such a package does not
// exist. The empty string is a valid package and typically is used to represent the
// 'current' package before it is known to the ResourceTable.
ResourceTablePackage* FindPackage(const android::StringPiece& name) const;
-
- ResourceTablePackage* FindPackageById(uint8_t id) const;
-
- ResourceTablePackage* CreatePackage(const android::StringPiece& name, Maybe<uint8_t> id = {});
-
- // Attempts to find a package having the specified name and ID. If not found, a new package
- // of the specified parameters is created and returned.
- ResourceTablePackage* CreatePackageAllowingDuplicateNames(const android::StringPiece& name,
- const Maybe<uint8_t> id);
+ ResourceTablePackage* FindOrCreatePackage(const android::StringPiece& name);
std::unique_ptr<ResourceTable> Clone() const;
+ // When a collision of resources occurs, this method decides which value to keep.
+ static CollisionResult ResolveValueCollision(Value* existing, Value* incoming);
+
// The string pool used by this resource table. Values that reference strings must use
// this pool to create their strings.
// NOTE: `string_pool` must come before `packages` so that it is destroyed after.
@@ -286,36 +312,9 @@
std::map<size_t, std::string> included_packages_;
private:
- // The function type that validates a symbol name. Returns a non-empty StringPiece representing
- // the offending character (which may be more than one byte in UTF-8). Returns an empty string
- // if the name was valid.
- using NameValidator = android::StringPiece(const android::StringPiece&);
-
- ResourceTablePackage* FindOrCreatePackage(const android::StringPiece& name);
-
- bool ValidateName(NameValidator validator, const ResourceNameRef& name, const Source& source,
- IDiagnostics* diag);
-
- bool AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
- const android::ConfigDescription& config,
- const android::StringPiece& product, std::unique_ptr<Value> value,
- NameValidator name_validator, const CollisionResolverFunc& conflict_resolver,
- IDiagnostics* diag);
-
- bool SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
- const ResourceId& res_id, NameValidator name_validator,
- IDiagnostics* diag);
-
- bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
- NameValidator name_validator, IDiagnostics* diag);
-
- bool SetOverlayableImpl(const ResourceNameRef &name, const OverlayableItem& overlayable,
- NameValidator name_validator, IDiagnostics *diag);
-
- // Controls whether the table validates resource names and prevents duplicate resource names
- bool validate_resources_ = true;
-
DISALLOW_COPY_AND_ASSIGN(ResourceTable);
+
+ Validation validation_ = Validation::kEnabled;
};
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 9271a7e..38391c9 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -37,31 +37,32 @@
TEST(ResourceTableTest, FailToAddResourceWithBadName) {
ResourceTable table;
- EXPECT_FALSE(table.AddResource(
- test::ParseNameOrDie("android:id/hey,there"), ConfigDescription{}, "",
- test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(),
- test::GetDiagnostics()));
+ EXPECT_FALSE(
+ table.AddResource(NewResourceBuilder(test::ParseNameOrDie("android:id/hey,there")).Build(),
+ test::GetDiagnostics()));
- EXPECT_FALSE(table.AddResource(
- test::ParseNameOrDie("android:id/hey:there"), ConfigDescription{}, "",
- test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(),
- test::GetDiagnostics()));
+ EXPECT_FALSE(
+ table.AddResource(NewResourceBuilder(test::ParseNameOrDie("android:id/hey:there")).Build(),
+ test::GetDiagnostics()));
}
TEST(ResourceTableTest, AddResourceWithWeirdNameWhenAddingMangledResources) {
ResourceTable table;
- EXPECT_TRUE(table.AddResourceMangled(
- test::ParseNameOrDie("android:id/heythere "), ConfigDescription{}, "",
- test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(), test::GetDiagnostics()));
+ EXPECT_TRUE(
+ table.AddResource(NewResourceBuilder(test::ParseNameOrDie("android:id/heythere "))
+ .SetAllowMangled(true)
+ .Build(),
+ test::GetDiagnostics()));
}
TEST(ResourceTableTest, AddOneResource) {
ResourceTable table;
EXPECT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:attr/id"), ConfigDescription{}, "",
- test::ValueBuilder<Id>().SetSource("test/path/file.xml", 23u).Build(),
+ NewResourceBuilder(test::ParseNameOrDie("android:attr/id"))
+ .SetValue(test::ValueBuilder<Id>().SetSource("test/path/file.xml", 23u).Build())
+ .Build(),
test::GetDiagnostics()));
EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/id"), NotNull());
@@ -70,32 +71,36 @@
TEST(ResourceTableTest, AddMultipleResources) {
ResourceTable table;
- ConfigDescription config;
ConfigDescription language_config;
memcpy(language_config.language, "pl", sizeof(language_config.language));
EXPECT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:attr/layout_width"), config, "",
- test::ValueBuilder<Id>().SetSource("test/path/file.xml", 10u).Build(),
- test::GetDiagnostics()));
-
- EXPECT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:attr/id"), config, "",
- test::ValueBuilder<Id>().SetSource("test/path/file.xml", 12u).Build(),
- test::GetDiagnostics()));
-
- EXPECT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/ok"), config, "",
- test::ValueBuilder<Id>().SetSource("test/path/file.xml", 14u).Build(),
- test::GetDiagnostics()));
-
- EXPECT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/ok"), language_config, "",
- test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
- .SetSource("test/path/file.xml", 20u)
+ NewResourceBuilder(test::ParseNameOrDie("android:attr/layout_width"))
+ .SetValue(test::ValueBuilder<Id>().SetSource("test/path/file.xml", 10u).Build())
.Build(),
test::GetDiagnostics()));
+ EXPECT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:attr/id"))
+ .SetValue(test::ValueBuilder<Id>().SetSource("test/path/file.xml", 12u).Build())
+ .Build(),
+ test::GetDiagnostics()));
+
+ EXPECT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/ok"))
+ .SetValue(test::ValueBuilder<Id>().SetSource("test/path/file.xml", 14u).Build())
+ .Build(),
+ test::GetDiagnostics()));
+
+ EXPECT_TRUE(
+ table.AddResource(NewResourceBuilder(test::ParseNameOrDie("android:string/ok"))
+ .SetValue(test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
+ .SetSource("test/path/file.xml", 20u)
+ .Build(),
+ language_config)
+ .Build(),
+ test::GetDiagnostics()));
+
EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/layout_width"), NotNull());
EXPECT_THAT(test::GetValue<Id>(&table, "android:attr/id"), NotNull());
EXPECT_THAT(test::GetValue<Id>(&table, "android:string/ok"), NotNull());
@@ -104,17 +109,19 @@
TEST(ResourceTableTest, OverrideWeakResourceValue) {
ResourceTable table;
-
- ASSERT_TRUE(table.AddResource(test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
- test::AttributeBuilder().SetWeak(true).Build(),
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(test::ParseNameOrDie("android:attr/foo"))
+ .SetValue(test::AttributeBuilder().SetWeak(true).Build())
+ .Build(),
test::GetDiagnostics()));
Attribute* attr = test::GetValue<Attribute>(&table, "android:attr/foo");
ASSERT_THAT(attr, NotNull());
EXPECT_TRUE(attr->IsWeak());
- ASSERT_TRUE(table.AddResource(test::ParseNameOrDie("android:attr/foo"), ConfigDescription{}, "",
- util::make_unique<Attribute>(), test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(test::ParseNameOrDie("android:attr/foo"))
+ .SetValue(util::make_unique<Attribute>())
+ .Build(),
+ test::GetDiagnostics()));
attr = test::GetValue<Attribute>(&table, "android:attr/foo");
ASSERT_THAT(attr, NotNull());
@@ -130,23 +137,27 @@
Attribute attr_two(android::ResTable_map::TYPE_STRING | android::ResTable_map::TYPE_REFERENCE);
attr_two.SetWeak(true);
- ASSERT_TRUE(table.AddResource(name, ConfigDescription{}, "",
- util::make_unique<Attribute>(attr_one), test::GetDiagnostics()));
- ASSERT_TRUE(table.AddResource(name, ConfigDescription{}, "",
- util::make_unique<Attribute>(attr_two), test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(name).SetValue(util::make_unique<Attribute>(attr_one)).Build(),
+ test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(name).SetValue(util::make_unique<Attribute>(attr_two)).Build(),
+ test::GetDiagnostics()));
}
TEST(ResourceTableTest, ProductVaryingValues) {
ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/foo"))
+ .SetValue(util::make_unique<Id>(), test::ParseConfigOrDie("land"), "tablet")
+ .Build(),
+ test::GetDiagnostics()));
- EXPECT_TRUE(table.AddResource(test::ParseNameOrDie("android:string/foo"),
- test::ParseConfigOrDie("land"), "tablet",
- util::make_unique<Id>(),
- test::GetDiagnostics()));
- EXPECT_TRUE(table.AddResource(test::ParseNameOrDie("android:string/foo"),
- test::ParseConfigOrDie("land"), "phone",
- util::make_unique<Id>(),
- test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/foo"))
+ .SetValue(util::make_unique<Id>(), test::ParseConfigOrDie("land"), "phone")
+ .Build(),
+ test::GetDiagnostics()));
EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "tablet"), NotNull());
EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "phone"), NotNull());
@@ -203,22 +214,26 @@
Visibility visibility;
visibility.level = Visibility::Level::kPrivate;
visibility.comment = "private";
- ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetVisibility(visibility).Build(),
+ test::GetDiagnostics()));
ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPrivate, "private"));
visibility.level = Visibility::Level::kUndefined;
visibility.comment = "undefined";
- ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetVisibility(visibility).Build(),
+ test::GetDiagnostics()));
ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPrivate, "private"));
visibility.level = Visibility::Level::kPublic;
visibility.comment = "public";
- ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetVisibility(visibility).Build(),
+ test::GetDiagnostics()));
ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPublic, "public"));
visibility.level = Visibility::Level::kPrivate;
visibility.comment = "private";
- ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetVisibility(visibility).Build(),
+ test::GetDiagnostics()));
ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPublic, "public"));
}
@@ -230,14 +245,16 @@
Maybe<ResourceTable::SearchResult> result;
allow_new.comment = "first";
- ASSERT_TRUE(table.SetAllowNew(name, allow_new, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetAllowNew(allow_new).Build(),
+ test::GetDiagnostics()));
result = table.FindResource(name);
ASSERT_TRUE(result);
ASSERT_TRUE(result.value().entry->allow_new);
ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("first"));
allow_new.comment = "second";
- ASSERT_TRUE(table.SetAllowNew(name, allow_new, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetAllowNew(allow_new).Build(),
+ test::GetDiagnostics()));
result = table.FindResource(name);
ASSERT_TRUE(result);
ASSERT_TRUE(result.value().entry->allow_new);
@@ -255,7 +272,8 @@
overlayable_item.source = Source("res/values/overlayable.xml", 42);
const ResourceName name = test::ParseNameOrDie("android:string/foo");
- ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetOverlayable(overlayable_item).Build(),
+ test::GetDiagnostics()));
Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
ASSERT_TRUE(search_result);
@@ -280,17 +298,20 @@
auto group = std::make_shared<Overlayable>("Name", "overlay://theme");
OverlayableItem overlayable(group);
overlayable.policies = PolicyFlags::PRODUCT_PARTITION;
- ASSERT_TRUE(table.SetOverlayable(foo, overlayable, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(foo).SetOverlayable(overlayable).Build(),
+ test::GetDiagnostics()));
const ResourceName bar = test::ParseNameOrDie("android:string/bar");
OverlayableItem overlayable2(group);
overlayable2.policies = PolicyFlags::PRODUCT_PARTITION;
- ASSERT_TRUE(table.SetOverlayable(bar, overlayable2, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(bar).SetOverlayable(overlayable2).Build(),
+ test::GetDiagnostics()));
const ResourceName baz = test::ParseNameOrDie("android:string/baz");
OverlayableItem overlayable3(group);
overlayable3.policies = PolicyFlags::VENDOR_PARTITION;
- ASSERT_TRUE(table.SetOverlayable(baz, overlayable3, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(baz).SetOverlayable(overlayable3).Build(),
+ test::GetDiagnostics()));
}
TEST(ResourceTableTest, SetOverlayableDifferentResourcesDifferentName) {
@@ -299,12 +320,14 @@
const ResourceName foo = test::ParseNameOrDie("android:string/foo");
OverlayableItem overlayable_item(std::make_shared<Overlayable>("Name", "overlay://theme"));
overlayable_item.policies = PolicyFlags::PRODUCT_PARTITION;
- ASSERT_TRUE(table.SetOverlayable(foo, overlayable_item, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(foo).SetOverlayable(overlayable_item).Build(),
+ test::GetDiagnostics()));
const ResourceName bar = test::ParseNameOrDie("android:string/bar");
OverlayableItem overlayable_item2(std::make_shared<Overlayable>("Name2", "overlay://theme"));
overlayable_item2.policies = PolicyFlags::PRODUCT_PARTITION;
- ASSERT_TRUE(table.SetOverlayable(bar, overlayable_item2, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(bar).SetOverlayable(overlayable_item2).Build(),
+ test::GetDiagnostics()));
}
TEST(ResourceTableTest, SetOverlayableSameResourcesFail) {
@@ -313,10 +336,12 @@
auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
OverlayableItem overlayable_item(overlayable);
- ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetOverlayable(overlayable_item).Build(),
+ test::GetDiagnostics()));
OverlayableItem overlayable_item2(overlayable);
- ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
+ ASSERT_FALSE(table.AddResource(NewResourceBuilder(name).SetOverlayable(overlayable_item2).Build(),
+ test::GetDiagnostics()));
}
TEST(ResourceTableTest, SetOverlayableSameResourcesDifferentNameFail) {
@@ -325,51 +350,38 @@
auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme");
OverlayableItem overlayable_item(overlayable);
- ASSERT_TRUE(table.SetOverlayable(name, overlayable_item, test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetOverlayable(overlayable_item).Build(),
+ test::GetDiagnostics()));
auto overlayable2 = std::make_shared<Overlayable>("Other", "overlay://theme");
OverlayableItem overlayable_item2(overlayable2);
- ASSERT_FALSE(table.SetOverlayable(name, overlayable_item2, test::GetDiagnostics()));
+ ASSERT_FALSE(table.AddResource(NewResourceBuilder(name).SetOverlayable(overlayable_item2).Build(),
+ test::GetDiagnostics()));
}
-TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
- ResourceTable table(/* validate_resources */ false);
+TEST(ResourceTableTest, ConflictingIds) {
+ ResourceTable table;
+ const ResourceName name = test::ParseNameOrDie("android:string/foo");
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetId(0x01010000).Build(),
+ test::GetDiagnostics()));
+ ASSERT_FALSE(table.AddResource(NewResourceBuilder(name).SetId(0x01010001).Build(),
+ test::GetDiagnostics()));
+}
- const ResourceName foo_name = test::ParseNameOrDie("android:bool/foo");
- ASSERT_TRUE(table.AddResourceWithId(foo_name, ResourceId(0x7f0100ff), ConfigDescription{} , "",
- test::BuildPrimitive(android::Res_value::TYPE_INT_BOOLEAN, 0),
- test::GetDiagnostics()));
- ASSERT_TRUE(table.AddResourceWithId(foo_name, ResourceId(0x7f010100), ConfigDescription{} , "",
- test::BuildPrimitive(android::Res_value::TYPE_INT_BOOLEAN, 1),
- test::GetDiagnostics()));
+TEST(ResourceTableTest, ConflictingIdsCreateEntry) {
+ ResourceTable table;
+ const ResourceName name = test::ParseNameOrDie("android:string/foo");
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(name).SetId(0x01010000, OnIdConflict::CREATE_ENTRY).Build(),
+ test::GetDiagnostics()));
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(name).SetId(0x01010001, OnIdConflict::CREATE_ENTRY).Build(),
+ test::GetDiagnostics()));
- ASSERT_TRUE(table.SetVisibilityWithId(foo_name, Visibility{Visibility::Level::kPublic},
- ResourceId(0x7f0100ff), test::GetDiagnostics()));
- ASSERT_TRUE(table.SetVisibilityWithId(foo_name, Visibility{Visibility::Level::kPrivate},
- ResourceId(0x7f010100), test::GetDiagnostics()));
-
- auto package = table.FindPackageById(0x7f);
- ASSERT_THAT(package, NotNull());
- auto type = package->FindType(ResourceType::kBool);
- ASSERT_THAT(type, NotNull());
-
- auto entry1 = type->FindEntry("foo", 0x00ff);
- ASSERT_THAT(entry1, NotNull());
- ASSERT_THAT(entry1->id, Eq(0x00ff));
- ASSERT_THAT(entry1->values[0], NotNull());
- ASSERT_THAT(entry1->values[0]->value, NotNull());
- ASSERT_THAT(ValueCast<BinaryPrimitive>(entry1->values[0]->value.get()), NotNull());
- ASSERT_THAT(ValueCast<BinaryPrimitive>(entry1->values[0]->value.get())->value.data, Eq(0u));
- ASSERT_THAT(entry1->visibility.level, Visibility::Level::kPublic);
-
- auto entry2 = type->FindEntry("foo", 0x0100);
- ASSERT_THAT(entry2, NotNull());
- ASSERT_THAT(entry2->id, Eq(0x0100));
- ASSERT_THAT(entry2->values[0], NotNull());
- ASSERT_THAT(entry1->values[0]->value, NotNull());
- ASSERT_THAT(ValueCast<BinaryPrimitive>(entry2->values[0]->value.get()), NotNull());
- ASSERT_THAT(ValueCast<BinaryPrimitive>(entry2->values[0]->value.get())->value.data, Eq(1u));
- ASSERT_THAT(entry2->visibility.level, Visibility::Level::kPrivate);
+ // Non-ambiguous query
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(name).SetId(0x01010001).SetValue(std::make_unique<Id>()).Build(),
+ test::GetDiagnostics()));
}
} // namespace aapt
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index b1e1a77..4247ec5 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -132,6 +132,11 @@
// The comment associated with the <public> tag.
string comment = 3;
+
+ // Indicates that the resource id may change across builds and that the public R.java identifier
+ // for this resource should not be final. This is set to `true` for resources in `staging-group`
+ // tags.
+ bool staged_api = 4;
}
// Whether a resource comes from a compile-time overlay and is explicitly allowed to not overlay an
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index ff54fcc..cd5015e 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -190,17 +190,6 @@
}
}
- // Ensure we have the compilation package at least.
- table.CreatePackage(context->GetCompilationPackage());
-
- // Assign an ID to any package that has resources.
- for (auto& pkg : table.packages) {
- if (!pkg->id) {
- // If no package ID was set while parsing (public identifiers), auto assign an ID.
- pkg->id = context->GetPackageId();
- }
- }
-
// Create the file/zip entry.
if (!writer->StartEntry(output_path, 0)) {
context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to open");
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 8cbd998..8975713 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -217,6 +217,7 @@
}, compiled_files_dir, &diag));
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+ ASSERT_NE(apk, nullptr);
ResourceTable* table = apk->GetResourceTable();
ASSERT_NE(table, nullptr);
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index d56994e..0bb044e 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -95,17 +95,17 @@
return false;
}
-static bool EmitResourceConfigValueDiff(IAaptContext* context, LoadedApk* apk_a,
- ResourceTablePackage* pkg_a, ResourceTableType* type_a,
- ResourceEntry* entry_a, ResourceConfigValue* config_value_a,
- LoadedApk* apk_b, ResourceTablePackage* pkg_b,
- ResourceTableType* type_b, ResourceEntry* entry_b,
- ResourceConfigValue* config_value_b) {
+static bool EmitResourceConfigValueDiff(
+ IAaptContext* context, LoadedApk* apk_a, const ResourceTablePackageView& pkg_a,
+ const ResourceTableTypeView& type_a, const ResourceEntry* entry_a,
+ const ResourceConfigValue* config_value_a, LoadedApk* apk_b,
+ const ResourceTablePackageView& pkg_b, const ResourceTableTypeView& type_b,
+ const ResourceEntry* entry_b, const ResourceConfigValue* config_value_b) {
Value* value_a = config_value_a->value.get();
Value* value_b = config_value_b->value.get();
if (!value_a->Equals(value_b)) {
std::stringstream str_stream;
- str_stream << "value " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
+ str_stream << "value " << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
<< " config=" << config_value_a->config << " does not match:\n";
value_a->Print(&str_stream);
str_stream << "\n vs \n";
@@ -117,16 +117,17 @@
}
static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a,
- ResourceTablePackage* pkg_a, ResourceTableType* type_a,
- ResourceEntry* entry_a, LoadedApk* apk_b,
- ResourceTablePackage* pkg_b, ResourceTableType* type_b,
- ResourceEntry* entry_b) {
+ const ResourceTablePackageView& pkg_a,
+ const ResourceTableTypeView& type_a, const ResourceEntry* entry_a,
+ LoadedApk* apk_b, const ResourceTablePackageView& pkg_b,
+ const ResourceTableTypeView& type_b,
+ const ResourceEntry* entry_b) {
bool diff = false;
- for (std::unique_ptr<ResourceConfigValue>& config_value_a : entry_a->values) {
- ResourceConfigValue* config_value_b = entry_b->FindValue(config_value_a->config);
+ for (const std::unique_ptr<ResourceConfigValue>& config_value_a : entry_a->values) {
+ auto config_value_b = entry_b->FindValue(config_value_a->config);
if (!config_value_b) {
std::stringstream str_stream;
- str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
+ str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
<< " config=" << config_value_a->config;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
@@ -138,35 +139,47 @@
}
// Check for any newly added config values.
- for (std::unique_ptr<ResourceConfigValue>& config_value_b : entry_b->values) {
- ResourceConfigValue* config_value_a = entry_a->FindValue(config_value_b->config);
+ for (const std::unique_ptr<ResourceConfigValue>& config_value_b : entry_b->values) {
+ auto config_value_a = entry_a->FindValue(config_value_b->config);
if (!config_value_a) {
std::stringstream str_stream;
- str_stream << "new config " << pkg_b->name << ":" << type_b->type << "/" << entry_b->name
+ str_stream << "new config " << pkg_b.name << ":" << type_b.type << "/" << entry_b->name
<< " config=" << config_value_b->config;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
}
- return false;
+ return diff;
}
static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a,
- ResourceTablePackage* pkg_a, ResourceTableType* type_a,
- LoadedApk* apk_b, ResourceTablePackage* pkg_b,
- ResourceTableType* type_b) {
+ const ResourceTablePackageView& pkg_a,
+ const ResourceTableTypeView& type_a, LoadedApk* apk_b,
+ const ResourceTablePackageView& pkg_b,
+ const ResourceTableTypeView& type_b) {
bool diff = false;
- for (std::unique_ptr<ResourceEntry>& entry_a : type_a->entries) {
- ResourceEntry* entry_b = type_b->FindEntry(entry_a->name);
- if (!entry_b) {
+ auto entry_a_iter = type_a.entries.begin();
+ auto entry_b_iter = type_b.entries.begin();
+ while (entry_a_iter != type_a.entries.end() || entry_b_iter != type_b.entries.end()) {
+ if (entry_b_iter == type_b.entries.end()) {
+ // Type A contains a type that type B does not have.
std::stringstream str_stream;
- str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name;
+ str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << (*entry_a_iter)->name;
+ EmitDiffLine(apk_a->GetSource(), str_stream.str());
+ diff = true;
+ } else if (entry_a_iter == type_a.entries.end()) {
+ // Type B contains a type that type A does not have.
+ std::stringstream str_stream;
+ str_stream << "new entry " << pkg_b.name << ":" << type_b.type << "/"
+ << (*entry_b_iter)->name;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else {
+ const auto& entry_a = *entry_a_iter;
+ const auto& entry_b = *entry_a_iter;
if (IsSymbolVisibilityDifferent(entry_a->visibility, entry_b->visibility)) {
std::stringstream str_stream;
- str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
+ str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
<< " has different visibility (";
if (entry_b->visibility.level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
@@ -185,7 +198,7 @@
} else if (IsIdDiff(entry_a->visibility.level, entry_a->id, entry_b->visibility.level,
entry_b->id)) {
std::stringstream str_stream;
- str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
+ str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
<< " has different public ID (";
if (entry_b->id) {
str_stream << "0x" << std::hex << entry_b->id.value();
@@ -202,46 +215,43 @@
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
- diff |= EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a.get(), apk_b, pkg_b,
- type_b, entry_b);
- }
- }
-
- // Check for any newly added entries.
- for (std::unique_ptr<ResourceEntry>& entry_b : type_b->entries) {
- ResourceEntry* entry_a = type_a->FindEntry(entry_b->name);
- if (!entry_a) {
- std::stringstream str_stream;
- str_stream << "new entry " << pkg_b->name << ":" << type_b->type << "/" << entry_b->name;
- EmitDiffLine(apk_b->GetSource(), str_stream.str());
- diff = true;
+ diff |= EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a, apk_b, pkg_b, type_b,
+ entry_b);
}
}
return diff;
}
static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a,
- ResourceTablePackage* pkg_a, LoadedApk* apk_b,
- ResourceTablePackage* pkg_b) {
+ const ResourceTablePackageView& pkg_a, LoadedApk* apk_b,
+ const ResourceTablePackageView& pkg_b) {
bool diff = false;
- for (std::unique_ptr<ResourceTableType>& type_a : pkg_a->types) {
- ResourceTableType* type_b = pkg_b->FindType(type_a->type);
- if (!type_b) {
+ auto type_a_iter = pkg_a.types.begin();
+ auto type_b_iter = pkg_b.types.begin();
+ while (type_a_iter != pkg_a.types.end() || type_b_iter != pkg_b.types.end()) {
+ if (type_b_iter == pkg_b.types.end()) {
+ // Type A contains a type that type B does not have.
std::stringstream str_stream;
- str_stream << "missing " << pkg_a->name << ":" << type_a->type;
+ str_stream << "missing " << pkg_a.name << ":" << type_a_iter->type;
EmitDiffLine(apk_a->GetSource(), str_stream.str());
diff = true;
+ } else if (type_a_iter == pkg_a.types.end()) {
+ // Type B contains a type that type A does not have.
+ std::stringstream str_stream;
+ str_stream << "new type " << pkg_b.name << ":" << type_b_iter->type;
+ EmitDiffLine(apk_b->GetSource(), str_stream.str());
+ diff = true;
} else {
- if (type_a->visibility_level != type_b->visibility_level) {
+ if (type_a_iter->visibility_level != type_b_iter->visibility_level) {
std::stringstream str_stream;
- str_stream << pkg_a->name << ":" << type_a->type << " has different visibility (";
- if (type_b->visibility_level == Visibility::Level::kPublic) {
+ str_stream << pkg_a.name << ":" << type_a_iter->type << " has different visibility (";
+ if (type_b_iter->visibility_level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
} else {
str_stream << "PRIVATE";
}
str_stream << " vs ";
- if (type_a->visibility_level == Visibility::Level::kPublic) {
+ if (type_a_iter->visibility_level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
} else {
str_stream << "PRIVATE";
@@ -249,18 +259,18 @@
str_stream << ")";
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
- } else if (IsIdDiff(type_a->visibility_level, type_a->id, type_b->visibility_level,
- type_b->id)) {
+ } else if (IsIdDiff(type_a_iter->visibility_level, type_a_iter->id,
+ type_b_iter->visibility_level, type_b_iter->id)) {
std::stringstream str_stream;
- str_stream << pkg_a->name << ":" << type_a->type << " has different public ID (";
- if (type_b->id) {
- str_stream << "0x" << std::hex << type_b->id.value();
+ str_stream << pkg_a.name << ":" << type_a_iter->type << " has different public ID (";
+ if (type_b_iter->id) {
+ str_stream << "0x" << std::hex << type_b_iter->id.value();
} else {
str_stream << "none";
}
str_stream << " vs ";
- if (type_a->id) {
- str_stream << "0x " << std::hex << type_a->id.value();
+ if (type_a_iter->id) {
+ str_stream << "0x " << std::hex << type_a_iter->id.value();
} else {
str_stream << "none";
}
@@ -268,47 +278,44 @@
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
- diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a.get(), apk_b, pkg_b, type_b);
- }
- }
-
- // Check for any newly added types.
- for (std::unique_ptr<ResourceTableType>& type_b : pkg_b->types) {
- ResourceTableType* type_a = pkg_a->FindType(type_b->type);
- if (!type_a) {
- std::stringstream str_stream;
- str_stream << "new type " << pkg_b->name << ":" << type_b->type;
- EmitDiffLine(apk_b->GetSource(), str_stream.str());
- diff = true;
+ diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, *type_a_iter, apk_b, pkg_b, *type_b_iter);
}
}
return diff;
}
static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, LoadedApk* apk_b) {
- ResourceTable* table_a = apk_a->GetResourceTable();
- ResourceTable* table_b = apk_b->GetResourceTable();
+ const auto table_a = apk_a->GetResourceTable()->GetPartitionedView();
+ const auto table_b = apk_b->GetResourceTable()->GetPartitionedView();
bool diff = false;
- for (std::unique_ptr<ResourceTablePackage>& pkg_a : table_a->packages) {
- ResourceTablePackage* pkg_b = table_b->FindPackage(pkg_a->name);
- if (!pkg_b) {
+ auto package_a_iter = table_a.packages.begin();
+ auto package_b_iter = table_b.packages.begin();
+ while (package_a_iter != table_a.packages.end() || package_b_iter != table_b.packages.end()) {
+ if (package_b_iter == table_b.packages.end()) {
+ // Table A contains a package that table B does not have.
std::stringstream str_stream;
- str_stream << "missing package " << pkg_a->name;
+ str_stream << "missing package " << package_a_iter->name;
+ EmitDiffLine(apk_a->GetSource(), str_stream.str());
+ diff = true;
+ } else if (package_a_iter == table_a.packages.end()) {
+ // Table B contains a package that table A does not have.
+ std::stringstream str_stream;
+ str_stream << "new package " << package_b_iter->name;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else {
- if (pkg_a->id != pkg_b->id) {
+ if (package_a_iter->id != package_b_iter->id) {
std::stringstream str_stream;
- str_stream << "package '" << pkg_a->name << "' has different id (";
- if (pkg_b->id) {
- str_stream << "0x" << std::hex << pkg_b->id.value();
+ str_stream << "package '" << package_a_iter->name << "' has different id (";
+ if (package_b_iter->id) {
+ str_stream << "0x" << std::hex << package_b_iter->id.value();
} else {
str_stream << "none";
}
str_stream << " vs ";
- if (pkg_a->id) {
- str_stream << "0x" << std::hex << pkg_a->id.value();
+ if (package_a_iter->id) {
+ str_stream << "0x" << std::hex << package_b_iter->id.value();
} else {
str_stream << "none";
}
@@ -316,20 +323,10 @@
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
}
- diff |= EmitResourcePackageDiff(context, apk_a, pkg_a.get(), apk_b, pkg_b);
+ diff |= EmitResourcePackageDiff(context, apk_a, *package_a_iter, apk_b, *package_b_iter);
}
}
- // Check for any newly added packages.
- for (std::unique_ptr<ResourceTablePackage>& pkg_b : table_b->packages) {
- ResourceTablePackage* pkg_a = table_a->FindPackage(pkg_b->name);
- if (!pkg_a) {
- std::stringstream str_stream;
- str_stream << "new package " << pkg_b->name;
- EmitDiffLine(apk_b->GetSource(), str_stream.str());
- diff = true;
- }
- }
return diff;
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 13e090d..ee6a764 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -25,6 +25,7 @@
#include <vector>
#include "android-base/errors.h"
+#include "android-base/expected.h"
#include "android-base/file.h"
#include "android-base/stringprintf.h"
#include "androidfw/Locale.h"
@@ -74,10 +75,25 @@
using ::aapt::io::FileInputStream;
using ::android::ConfigDescription;
using ::android::StringPiece;
+using ::android::base::expected;
using ::android::base::StringPrintf;
+using ::android::base::unexpected;
namespace aapt {
+namespace {
+
+expected<ResourceTablePackage*, const char*> GetStaticLibraryPackage(ResourceTable* table) {
+ // Resource tables built by aapt2 always contain one package. This is a post condition of
+ // VerifyNoExternalPackages.
+ if (table->packages.size() != 1u) {
+ return unexpected("static library contains more than one package");
+ }
+ return table->packages.back().get();
+}
+
+} // namespace
+
constexpr uint8_t kAndroidPackageId = 0x01;
class LinkContext : public IAaptContext {
@@ -633,13 +649,18 @@
const ResourceFile& file = doc->file;
dst_path = ResourceUtils::BuildResourceFileName(file, context_->GetNameMangler());
- std::unique_ptr<FileReference> file_ref =
+ auto file_ref =
util::make_unique<FileReference>(table->string_pool.MakeRef(dst_path));
file_ref->SetSource(doc->file.source);
+
// Update the output format of this XML file.
file_ref->type = XmlFileTypeForOutputFormat(options_.output_format);
- if (!table->AddResourceMangled(file.name, file.config, {}, std::move(file_ref),
- context_->GetDiagnostics())) {
+ bool result = table->AddResource(NewResourceBuilder(file.name)
+ .SetValue(std::move(file_ref), file.config)
+ .SetAllowMangled(true)
+ .Build(),
+ context_->GetDiagnostics());
+ if (!result) {
return false;
}
}
@@ -842,18 +863,15 @@
ResourceTable* table = static_apk->GetResourceTable();
// If we are using --no-static-lib-packages, we need to rename the package of this table to
- // our compilation package.
- if (options_.no_static_lib_packages) {
- // Since package names can differ, and multiple packages can exist in a ResourceTable,
- // we place the requirement that all static libraries are built with the package
- // ID 0x7f. So if one is not found, this is an error.
- if (ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId)) {
- pkg->name = context_->GetCompilationPackage();
- } else {
- context_->GetDiagnostics()->Error(DiagMessage(path)
- << "no package with ID 0x7f found in static library");
+ // our compilation package so the symbol package name does not get mangled into the entry
+ // name.
+ if (options_.no_static_lib_packages && !table->packages.empty()) {
+ auto lib_package_result = GetStaticLibraryPackage(table);
+ if (!lib_package_result.has_value()) {
+ context_->GetDiagnostics()->Error(DiagMessage(path) << lib_package_result.error());
return false;
}
+ lib_package_result.value()->name = context_->GetCompilationPackage();
}
context_->GetExternalSymbols()->AppendSource(
@@ -982,8 +1000,7 @@
// stripped, or there is an error and false is returned.
bool VerifyNoExternalPackages() {
auto is_ext_package_func = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
- return context_->GetCompilationPackage() != pkg->name || !pkg->id ||
- pkg->id.value() != context_->GetPackageId();
+ return context_->GetCompilationPackage() != pkg->name;
};
bool error = false;
@@ -1027,19 +1044,11 @@
bool VerifyNoIdsSet() {
for (const auto& package : final_table_.packages) {
for (const auto& type : package->types) {
- if (type->id) {
- context_->GetDiagnostics()->Error(DiagMessage() << "type " << type->type << " has ID "
- << StringPrintf("%02x", type->id.value())
- << " assigned");
- return false;
- }
-
for (const auto& entry : type->entries) {
if (entry->id) {
ResourceNameRef res_name(package->name, type->type, entry->name);
- context_->GetDiagnostics()->Error(
- DiagMessage() << "entry " << res_name << " has ID "
- << StringPrintf("%02x", entry->id.value()) << " assigned");
+ context_->GetDiagnostics()->Error(DiagMessage() << "resource " << res_name << " has ID "
+ << entry->id.value() << " assigned");
return false;
}
}
@@ -1313,12 +1322,17 @@
}
ResourceTable* table = apk->GetResourceTable();
- ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId);
- if (!pkg) {
- context_->GetDiagnostics()->Error(DiagMessage(input) << "static library has no package");
+ if (table->packages.empty()) {
+ return true;
+ }
+
+ auto lib_package_result = GetStaticLibraryPackage(table);
+ if (!lib_package_result.has_value()) {
+ context_->GetDiagnostics()->Error(DiagMessage(input) << lib_package_result.error());
return false;
}
+ ResourceTablePackage* pkg = lib_package_result.value();
bool result;
if (options_.no_static_lib_packages) {
// Merge all resources as if they were in the compilation package. This is the old behavior
@@ -1365,11 +1379,11 @@
res_name = mangled_name.value();
}
- std::unique_ptr<Id> id = util::make_unique<Id>();
+ auto id = util::make_unique<Id>();
id->SetSource(source.WithLine(exported_symbol.line));
- bool result =
- final_table_.AddResourceMangled(res_name, ConfigDescription::DefaultConfig(),
- std::string(), std::move(id), context_->GetDiagnostics());
+ bool result = final_table_.AddResource(
+ NewResourceBuilder(res_name).SetValue(std::move(id)).SetAllowMangled(true).Build(),
+ context_->GetDiagnostics());
if (!result) {
return false;
}
@@ -1750,7 +1764,7 @@
// anything else being generated, which includes the Java classes.
// If required, the package name is modifed before flattening, and then modified back
// to its original name.
- ResourceTablePackage* package_to_rewrite = nullptr;
+ std::optional<ResourceTablePackageView> package_to_rewrite;
// Pre-O, the platform treats negative resource IDs [those with a package ID of 0x80
// or higher] as invalid. In order to work around this limitation, we allow the use
// of traditionally reserved resource IDs [those between 0x02 and 0x7E]. Allow the
@@ -1764,10 +1778,11 @@
// The base APK is included, and this is a feature split. If the base package is
// the same as this package, then we are building an old style Android Instant Apps feature
// split and must apply this workaround to avoid requiring namespaces support.
- package_to_rewrite = table->FindPackage(context_->GetCompilationPackage());
- if (package_to_rewrite != nullptr) {
+ auto table_view = table->GetPartitionedView();
+ if (!table_view.packages.empty() &&
+ table_view.packages.back().name == context_->GetCompilationPackage()) {
+ package_to_rewrite = std::move(table_view.packages.back());
CHECK_EQ(1u, table->packages.size()) << "can't change name of package when > 1 package";
-
std::string new_package_name =
StringPrintf("%s.%s", package_to_rewrite->name.c_str(),
app_info_.split_name.value_or_default("feature").c_str());
@@ -1783,7 +1798,7 @@
bool success = FlattenTable(table, options_.output_format, writer);
- if (package_to_rewrite != nullptr) {
+ if (package_to_rewrite.has_value()) {
// Change the name back.
package_to_rewrite->name = context_->GetCompilationPackage();
if (package_to_rewrite->id) {
@@ -1925,8 +1940,7 @@
for (auto& entry : type->entries) {
ResourceName name(package->name, type->type, entry->name);
// The IDs are guaranteed to exist.
- options_.stable_id_map[std::move(name)] =
- ResourceId(package->id.value(), type->id.value(), entry->id.value());
+ options_.stable_id_map[std::move(name)] = entry->id.value();
}
}
}
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 73072a9..dfdac6b 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -25,6 +25,7 @@
using testing::Eq;
using testing::HasSubstr;
using testing::Ne;
+using testing::NotNull;
namespace aapt {
@@ -47,6 +48,8 @@
// Load the binary xml tree
android::ResXMLTree tree;
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+ ASSERT_THAT(apk, Ne(nullptr));
+
std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
ASSERT_THAT(data, Ne(nullptr));
AssertLoadXml(apk.get(), data.get(), &tree);
@@ -73,6 +76,8 @@
// Load the binary xml tree
android::ResXMLTree tree;
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+ ASSERT_THAT(apk, Ne(nullptr));
+
std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
ASSERT_THAT(data, Ne(nullptr));
AssertLoadXml(apk.get(), data.get(), &tree);
@@ -208,6 +213,8 @@
ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+ ASSERT_THAT(apk, Ne(nullptr));
+
const Style* actual_style = test::GetValue<Style>(
apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
ASSERT_NE(actual_style, nullptr);
@@ -250,6 +257,8 @@
ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+ ASSERT_THAT(apk, Ne(nullptr));
+
const Style* actual_style = test::GetValue<Style>(
apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
ASSERT_NE(actual_style, nullptr);
@@ -392,4 +401,127 @@
EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
}
+TEST_F(LinkTest, StagedAndroidApi) {
+ StdErrDiagnostics diag;
+ const std::string android_values =
+ R"(<resources>
+ <public type="attr" name="finalized_res" id="0x01010001"/>
+
+ <!-- S staged attributes (support staged resources in the same type id) -->
+ <staging-public-group type="attr" first-id="0x01010050">
+ <public name="staged_s_res" />
+ </staging-public-group>
+
+ <!-- SV2 staged attributes (support staged resources in a separate type id) -->
+ <staging-public-group type="attr" first-id="0x01ff0049">
+ <public name="staged_s2_res" />
+ </staging-public-group>
+
+ <!-- T staged attributes (support staged resources in multiple separate type ids) -->
+ <staging-public-group type="attr" first-id="0x01fe0063">
+ <public name="staged_t_res" />
+ </staging-public-group>
+
+ <staging-public-group type="string" first-id="0x01fd0072">
+ <public name="staged_t_string" />
+ </staging-public-group>
+
+ <attr name="finalized_res" />
+ <attr name="staged_s_res" />
+ <attr name="staged_s2_res" />
+ <attr name="staged_t_res" />
+ <string name="staged_t_string">Hello</string>
+ </resources>)";
+
+ const std::string app_values =
+ R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <attr name="bar" />
+ <declare-styleable name="ClientStyleable">
+ <attr name="android:finalized_res" />
+ <attr name="android:staged_s_res" />
+ <attr name="bar" />
+ </declare-styleable>
+ </resources>)";
+
+ const std::string android_res = GetTestPath("android-res");
+ ASSERT_TRUE(
+ CompileFile(GetTestPath("res/values/values.xml"), android_values, android_res, &diag));
+
+ const std::string android_apk = GetTestPath("android.apk");
+ const std::string android_java = GetTestPath("android_java");
+ // clang-format off
+ auto android_manifest = ManifestBuilder(this)
+ .SetPackageName("android")
+ .Build();
+
+ auto android_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(android_manifest)
+ .AddParameter("--private-symbols", "com.android.internal")
+ .AddParameter("--java", android_java)
+ .AddCompiledResDir(android_res, &diag)
+ .Build(android_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(android_link_args, &diag));
+
+ const std::string android_r_java = android_java + "/android/R.java";
+ std::string android_r_contents;
+ ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static final int finalized_res=0x01010001;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_s_res=0x01010050;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_s2_res=0x01ff0049;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_t_res=0x01fe0063;"));
+ EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_t_string=0x01fd0072;"));
+
+ // Build an app that uses the framework attribute in a declare-styleable
+ const std::string client_res = GetTestPath("app-res");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), app_values, client_res, &diag));
+
+ const std::string app_apk = GetTestPath("app.apk");
+ const std::string app_java = GetTestPath("app_java");
+ // clang-format off
+ auto app_manifest = ManifestBuilder(this)
+ .SetPackageName("com.example.app")
+ .Build();
+
+ auto app_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(app_manifest)
+ .AddParameter("--java", app_java)
+ .AddParameter("-I", android_apk)
+ .AddCompiledResDir(client_res, &diag)
+ .Build(app_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(app_link_args, &diag));
+
+ const std::string client_r_java = app_java + "/com/example/app/R.java";
+ std::string client_r_contents;
+ ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
+ EXPECT_THAT(client_r_contents, HasSubstr(" 0x01010001, android.R.attr.staged_s_res, 0x7f010000"));
+
+ // Test that the resource ids of staged and non-staged resource can be retrieved
+ android::AssetManager2 am;
+ auto android_asset = android::ApkAssets::Load(android_apk);
+ ASSERT_THAT(android_asset, NotNull());
+ ASSERT_TRUE(am.SetApkAssets({android_asset.get()}));
+
+ auto result = am.GetResourceId("android:attr/finalized_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01010001));
+
+ result = am.GetResourceId("android:attr/staged_s_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01010050));
+
+ result = am.GetResourceId("android:attr/staged_s2_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01ff0049));
+
+ result = am.GetResourceId("android:attr/staged_t_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01fe0063));
+
+ result = am.GetResourceId("android:string/staged_t_string");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01fd0072));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 17c22c5..9a50b26 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -17,76 +17,135 @@
#include "compile/IdAssigner.h"
#include <map>
+#include <unordered_map>
+#include "android-base/expected.h"
#include "android-base/logging.h"
#include "ResourceTable.h"
#include "process/IResourceTableConsumer.h"
#include "util/Util.h"
+using android::base::expected;
+using android::base::unexpected;
+
namespace aapt {
-/**
- * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and
- * ResourceEntry,
- * as long as there is no existing ID or the ID is the same.
- */
-static bool AssignId(IDiagnostics* diag, const ResourceId& id,
- const ResourceName& name, ResourceTablePackage* pkg,
- ResourceTableType* type, ResourceEntry* entry) {
- if (pkg->id.value() == id.package_id()) {
- if (!type->id || type->id.value() == id.type_id()) {
- type->id = id.type_id();
+namespace {
+template <typename T>
+using Result = expected<T, std::string>;
- if (!entry->id || entry->id.value() == id.entry_id()) {
- entry->id = id.entry_id();
- return true;
- }
- }
+template <typename Id, typename Key>
+struct NextIdFinder {
+ explicit NextIdFinder(Id start_id = 0u) : next_id_(start_id){};
+
+ // Attempts to reserve an identifier for the specified key.
+ // If the identifier is already reserved by a different key, an error message is returned.
+ // Reserving identifiers must be completed before `NextId` is called for the first time.
+ Result<Id> ReserveId(Key key, Id id);
+
+ // Retrieves the next available identifier that has not been reserved.
+ std::optional<Id> NextId();
+
+ private:
+ // Attempts to set `next_id_` to the next available identifier that has not been reserved.
+ // Returns whether there were any available identifiers.
+ std::optional<Id> SkipToNextAvailableId();
+
+ Id next_id_;
+ bool next_id_called_ = false;
+ bool exhausted_ = false;
+ std::map<Id, Key> pre_assigned_ids_;
+ typename std::map<Id, Key>::iterator next_preassigned_id_;
+};
+
+struct TypeGroup {
+ explicit TypeGroup(uint8_t package_id, uint8_t type_id)
+ : package_id_(package_id), type_id_(type_id){};
+
+ // Attempts to reserve the resource id for the specified resource name.
+ // If the id is already reserved by a different name, an error message is returned.
+ // Reserving identifiers must be completed before `NextId` is called for the first time.
+ Result<std::monostate> ReserveId(const ResourceName& name, ResourceId id);
+
+ // Retrieves the next available resource id that has not been reserved.
+ Result<ResourceId> NextId();
+
+ private:
+ uint8_t package_id_;
+ uint8_t type_id_;
+ NextIdFinder<uint16_t, ResourceName> next_entry_id_;
+};
+
+struct ResourceTypeKey {
+ ResourceType type;
+ uint8_t id;
+
+ bool operator<(const ResourceTypeKey& other) const {
+ return (type != other.type) ? type < other.type : id < other.id;
}
- const ResourceId existing_id(pkg->id.value(), type->id ? type->id.value() : 0,
- entry->id ? entry->id.value() : 0);
- diag->Error(DiagMessage() << "can't assign ID " << id << " to resource "
- << name << " with conflicting ID " << existing_id);
- return false;
+ bool operator==(const ResourceTypeKey& other) const {
+ return type == other.type && id == other.id;
+ }
+
+ bool operator!=(const ResourceTypeKey& other) const {
+ return !(*this == other);
+ }
+};
+
+::std::ostream& operator<<(::std::ostream& out, const ResourceTypeKey& type) {
+ return out << type.type;
}
+struct IdAssignerContext {
+ IdAssignerContext(std::string package_name, uint8_t package_id)
+ : package_name_(std::move(package_name)), package_id_(package_id) {
+ }
+
+ // Attempts to reserve the resource id for the specified resource name.
+ // Returns whether the id was reserved successfully.
+ // Reserving identifiers must be completed before `NextId` is called for the first time.
+ bool ReserveId(const ResourceName& name, ResourceId id, const Visibility& visibility,
+ IDiagnostics* diag);
+
+ // Retrieves the next available resource id that has not been reserved.
+ std::optional<ResourceId> NextId(const ResourceName& name, IDiagnostics* diag);
+
+ private:
+ std::string package_name_;
+ uint8_t package_id_;
+ std::map<ResourceTypeKey, TypeGroup> types_;
+ std::map<ResourceType, uint8_t> non_staged_type_ids_;
+ NextIdFinder<uint8_t, ResourceTypeKey> type_id_finder_ =
+ NextIdFinder<uint8_t, ResourceTypeKey>(1);
+};
+
+} // namespace
+
bool IdAssigner::Consume(IAaptContext* context, ResourceTable* table) {
- std::map<ResourceId, ResourceName> assigned_ids;
-
+ IdAssignerContext assigned_ids(context->GetCompilationPackage(), context->GetPackageId());
for (auto& package : table->packages) {
- CHECK(bool(package->id)) << "packages must have manually assigned IDs";
-
for (auto& type : package->types) {
for (auto& entry : type->entries) {
const ResourceName name(package->name, type->type, entry->name);
+ if (entry->id) {
+ if (!assigned_ids.ReserveId(name, entry->id.value(), entry->visibility,
+ context->GetDiagnostics())) {
+ return false;
+ }
+ }
if (assigned_id_map_) {
// Assign the pre-assigned stable ID meant for this resource.
const auto iter = assigned_id_map_->find(name);
if (iter != assigned_id_map_->end()) {
const ResourceId assigned_id = iter->second;
- const bool result =
- AssignId(context->GetDiagnostics(), assigned_id, name,
- package.get(), type.get(), entry.get());
- if (!result) {
+ if (!assigned_ids.ReserveId(name, assigned_id, entry->visibility,
+ context->GetDiagnostics())) {
return false;
}
- }
- }
-
- if (package->id && type->id && entry->id) {
- // If the ID is set for this resource, then reserve it.
- ResourceId resource_id(package->id.value(), type->id.value(),
- entry->id.value());
- auto result = assigned_ids.insert({resource_id, name});
- const ResourceName& existing_name = result.first->second;
- if (!result.second) {
- context->GetDiagnostics()->Error(
- DiagMessage() << "resource " << name << " has same ID "
- << resource_id << " as " << existing_name);
- return false;
+ entry->id = assigned_id;
}
}
}
@@ -94,125 +153,185 @@
}
if (assigned_id_map_) {
- // Reserve all the IDs mentioned in the stable ID map. That way we won't
- // assign
- // IDs that were listed in the map if they don't exist in the table.
+ // Reserve all the IDs mentioned in the stable ID map. That way we won't assig IDs that were
+ // listed in the map if they don't exist in the table.
for (const auto& stable_id_entry : *assigned_id_map_) {
const ResourceName& pre_assigned_name = stable_id_entry.first;
const ResourceId& pre_assigned_id = stable_id_entry.second;
- auto result = assigned_ids.insert({pre_assigned_id, pre_assigned_name});
- const ResourceName& existing_name = result.first->second;
- if (!result.second && existing_name != pre_assigned_name) {
- context->GetDiagnostics()->Error(
- DiagMessage() << "stable ID " << pre_assigned_id << " for resource "
- << pre_assigned_name
- << " is already taken by resource " << existing_name);
+ if (!assigned_ids.ReserveId(pre_assigned_name, pre_assigned_id, {},
+ context->GetDiagnostics())) {
return false;
}
}
}
- // Assign any resources without IDs the next available ID. Gaps will be filled
- // if possible,
+ // Assign any resources without IDs the next available ID. Gaps will be filled if possible,
// unless those IDs have been reserved.
-
- const auto assigned_ids_iter_end = assigned_ids.end();
for (auto& package : table->packages) {
- CHECK(bool(package->id)) << "packages must have manually assigned IDs";
-
- // Build a half filled ResourceId object, which will be used to find the
- // closest matching
- // reserved ID in the assignedId map. From that point the next available
- // type ID can be
- // found.
- ResourceId resource_id(package->id.value(), 0, 0);
- uint8_t next_expected_type_id = 1;
-
- // Find the closest matching ResourceId that is <= the one with only the
- // package set.
- auto next_type_iter = assigned_ids.lower_bound(resource_id);
for (auto& type : package->types) {
- if (!type->id) {
- // We need to assign a type ID. Iterate over the reserved IDs until we
- // find
- // some type ID that is a distance of 2 greater than the last one we've
- // seen.
- // That means there is an available type ID between these reserved IDs.
- while (next_type_iter != assigned_ids_iter_end) {
- if (next_type_iter->first.package_id() != package->id.value()) {
- break;
- }
-
- const uint8_t type_id = next_type_iter->first.type_id();
- if (type_id > next_expected_type_id) {
- // There is a gap in the type IDs, so use the missing one.
- type->id = next_expected_type_id++;
- break;
- }
-
- // Set our expectation to be the next type ID after the reserved one
- // we
- // just saw.
- next_expected_type_id = type_id + 1;
-
- // Move to the next reserved ID.
- ++next_type_iter;
- }
-
- if (!type->id) {
- // We must have hit the end of the reserved IDs and not found a gap.
- // That means the next ID is available.
- type->id = next_expected_type_id++;
- }
- }
-
- resource_id = ResourceId(package->id.value(), type->id.value(), 0);
- uint16_t next_expected_entry_id = 0;
-
- // Find the closest matching ResourceId that is <= the one with only the
- // package
- // and type set.
- auto next_entry_iter = assigned_ids.lower_bound(resource_id);
for (auto& entry : type->entries) {
- if (!entry->id) {
- // We need to assign an entry ID. Iterate over the reserved IDs until
- // we find
- // some entry ID that is a distance of 2 greater than the last one
- // we've seen.
- // That means there is an available entry ID between these reserved
- // IDs.
- while (next_entry_iter != assigned_ids_iter_end) {
- if (next_entry_iter->first.package_id() != package->id.value() ||
- next_entry_iter->first.type_id() != type->id.value()) {
- break;
- }
-
- const uint16_t entry_id = next_entry_iter->first.entry_id();
- if (entry_id > next_expected_entry_id) {
- // There is a gap in the entry IDs, so use the missing one.
- entry->id = next_expected_entry_id++;
- break;
- }
-
- // Set our expectation to be the next type ID after the reserved one
- // we
- // just saw.
- next_expected_entry_id = entry_id + 1;
-
- // Move to the next reserved entry ID.
- ++next_entry_iter;
- }
-
- if (!entry->id) {
- // We must have hit the end of the reserved IDs and not found a gap.
- // That means the next ID is available.
- entry->id = next_expected_entry_id++;
- }
+ const ResourceName name(package->name, type->type, entry->name);
+ if (entry->id) {
+ continue;
}
+ auto id = assigned_ids.NextId(name, context->GetDiagnostics());
+ if (!id.has_value()) {
+ return false;
+ }
+ entry->id = id.value();
}
}
}
return true;
}
+namespace {
+template <typename Id, typename Key>
+Result<Id> NextIdFinder<Id, Key>::ReserveId(Key key, Id id) {
+ CHECK(!next_id_called_) << "ReserveId cannot be called after NextId";
+ auto assign_result = pre_assigned_ids_.emplace(id, key);
+ if (!assign_result.second && assign_result.first->second != key) {
+ std::stringstream error;
+ error << "ID is already assigned to " << assign_result.first->second;
+ return unexpected(error.str());
+ }
+ return id;
+}
+
+template <typename Id, typename Key>
+std::optional<Id> NextIdFinder<Id, Key>::NextId() {
+ if (!next_id_called_) {
+ next_id_called_ = true;
+ next_preassigned_id_ = pre_assigned_ids_.begin();
+ }
+ return SkipToNextAvailableId();
+}
+
+template <typename Id, typename Key>
+std::optional<Id> NextIdFinder<Id, Key>::SkipToNextAvailableId() {
+ if (exhausted_) {
+ return {};
+ }
+ while (next_preassigned_id_ != pre_assigned_ids_.end()) {
+ if (next_preassigned_id_->first == next_id_) {
+ if (next_id_ == std::numeric_limits<Id>::max()) {
+ // The last identifier was reserved so there are no more available identifiers.
+ exhausted_ = true;
+ return {};
+ }
+ ++next_id_;
+ ++next_preassigned_id_;
+ continue;
+ }
+ CHECK(next_preassigned_id_->first > next_id_) << "Preassigned IDs are not in sorted order";
+ break;
+ }
+ if (next_id_ == std::numeric_limits<Id>::max()) {
+ // There are no more identifiers after this one, but this one is still available so return it.
+ exhausted_ = true;
+ }
+ return next_id_++;
+}
+
+Result<std::monostate> TypeGroup::ReserveId(const ResourceName& name, ResourceId id) {
+ if (type_id_ != id.type_id()) {
+ // Currently there cannot be multiple type ids for a single type.
+ std::stringstream error;
+ error << "type '" << name.type << "' already has ID " << std::hex << (int)id.type_id();
+ return unexpected(error.str());
+ }
+
+ auto assign_result = next_entry_id_.ReserveId(name, id.entry_id());
+ if (!assign_result.has_value()) {
+ std::stringstream error;
+ error << "entry " << assign_result.error();
+ return unexpected(error.str());
+ }
+ return {};
+}
+
+Result<ResourceId> TypeGroup::NextId() {
+ auto entry_id = next_entry_id_.NextId();
+ if (!entry_id.has_value()) {
+ std::stringstream error;
+ error << "resource type ID has exceeded the maximum number of resource entries ("
+ << (std::numeric_limits<uint16_t>::max() + 1u) << ")";
+ return unexpected(error.str());
+ }
+ return ResourceId(package_id_, type_id_, entry_id.value());
+}
+
+bool IdAssignerContext::ReserveId(const ResourceName& name, ResourceId id,
+ const Visibility& visibility, IDiagnostics* diag) {
+ if (package_id_ != id.package_id()) {
+ diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
+ << " because package already has ID " << std::hex
+ << (int)id.package_id());
+ return false;
+ }
+
+ auto key = ResourceTypeKey{name.type, id.type_id()};
+ auto type = types_.find(key);
+ if (type == types_.end()) {
+ // The type has not been assigned an id yet. Ensure that the specified id is not being used by
+ // another type.
+ auto assign_result = type_id_finder_.ReserveId(key, id.type_id());
+ if (!assign_result.has_value()) {
+ diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
+ << " because type " << assign_result.error());
+ return false;
+ }
+ type = types_.emplace(key, TypeGroup(package_id_, id.type_id())).first;
+ }
+
+ if (!visibility.staged_api) {
+ // Ensure that non-staged resources can only exist in one type ID.
+ auto non_staged_type = non_staged_type_ids_.emplace(name.type, id.type_id());
+ if (!non_staged_type.second && non_staged_type.first->second != id.type_id()) {
+ diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
+ << " because type already has ID " << std::hex
+ << (int)id.type_id());
+ return false;
+ }
+ }
+
+ auto assign_result = type->second.ReserveId(name, id);
+ if (!assign_result.has_value()) {
+ diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name << " because "
+ << assign_result.error());
+ return false;
+ }
+
+ return true;
+}
+
+std::optional<ResourceId> IdAssignerContext::NextId(const ResourceName& name, IDiagnostics* diag) {
+ // The package name is not known during the compile stage.
+ // Resources without a package name are considered a part of the app being linked.
+ CHECK(name.package.empty() || name.package == package_name_);
+
+ // Find the type id for non-staged resources of this type.
+ auto non_staged_type = non_staged_type_ids_.find(name.type);
+ if (non_staged_type == non_staged_type_ids_.end()) {
+ auto next_type_id = type_id_finder_.NextId();
+ CHECK(next_type_id.has_value()) << "resource type IDs allocated have exceeded maximum (256)";
+ non_staged_type = non_staged_type_ids_.emplace(name.type, *next_type_id).first;
+ }
+
+ ResourceTypeKey key{name.type, non_staged_type->second};
+ auto type = types_.find(key);
+ if (type == types_.end()) {
+ type = types_.emplace(key, TypeGroup(package_id_, key.id)).first;
+ }
+
+ auto assign_result = type->second.NextId();
+ if (!assign_result.has_value()) {
+ diag->Error(DiagMessage() << "can't assign resource ID to resource " << name << " because "
+ << assign_result.error());
+ return {};
+ }
+ return assign_result.value();
+}
+} // namespace
+
} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index 5cff004..6637766 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -20,42 +20,40 @@
namespace aapt {
+struct IdAssignerTests : public ::testing::Test {
+ void SetUp() override {
+ context = test::ContextBuilder().SetCompilationPackage("android").SetPackageId(0x01).Build();
+ }
+ std::unique_ptr<IAaptContext> context;
+};
+
::testing::AssertionResult VerifyIds(ResourceTable* table);
-TEST(IdAssignerTest, AssignIds) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .AddSimple("android:attr/foo")
- .AddSimple("android:attr/bar")
- .AddSimple("android:id/foo")
- .SetPackageId("android", 0x01)
- .Build();
-
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+TEST_F(IdAssignerTests, AssignIds) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo")
+ .AddSimple("android:attr/bar")
+ .AddSimple("android:id/foo")
+ .Build();
IdAssigner assigner;
ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
ASSERT_TRUE(VerifyIds(table.get()));
}
-TEST(IdAssignerTest, AssignIdsWithReservedIds) {
- std::unique_ptr<ResourceTable> table =
- test::ResourceTableBuilder()
- .AddSimple("android:id/foo", ResourceId(0x01010000))
- .AddSimple("android:dimen/two")
- .AddSimple("android:integer/three")
- .AddSimple("android:string/five")
- .AddSimple("android:attr/fun", ResourceId(0x01040000))
- .AddSimple("android:attr/foo", ResourceId(0x01040006))
- .AddSimple("android:attr/bar")
- .AddSimple("android:attr/baz")
- .AddSimple("app:id/biz")
- .SetPackageId("android", 0x01)
- .SetPackageId("app", 0x7f)
- .Build();
+TEST_F(IdAssignerTests, AssignIdsWithReservedIds) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:id/foo", ResourceId(0x01010000))
+ .AddSimple("android:dimen/two")
+ .AddSimple("android:integer/three")
+ .AddSimple("android:string/five")
+ .AddSimple("android:attr/fun", ResourceId(0x01040000))
+ .AddSimple("android:attr/foo", ResourceId(0x01040006))
+ .AddSimple("android:attr/bar")
+ .AddSimple("android:attr/baz")
+ .Build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
IdAssigner assigner;
-
ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
ASSERT_TRUE(VerifyIds(table.get()));
@@ -65,12 +63,12 @@
maybe_result = table->FindResource(test::ParseNameOrDie("android:dimen/two"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<uint8_t>(2), maybe_result.value().type->id);
+ EXPECT_EQ(make_value<ResourceId>(0x01020000), maybe_result.value().entry->id);
maybe_result =
table->FindResource(test::ParseNameOrDie("android:integer/three"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<uint8_t>(3), maybe_result.value().type->id);
+ EXPECT_EQ(make_value<ResourceId>(0x01030000), maybe_result.value().entry->id);
// Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX
// IDs.
@@ -78,107 +76,133 @@
maybe_result =
table->FindResource(test::ParseNameOrDie("android:string/five"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<uint8_t>(5), maybe_result.value().type->id);
+ EXPECT_EQ(make_value<ResourceId>(0x01050000), maybe_result.value().entry->id);
// Expect to fill in the gaps between 0x01040000 and 0x01040006.
maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/bar"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<uint16_t>(1), maybe_result.value().entry->id);
+ EXPECT_EQ(make_value<ResourceId>(0x01040001), maybe_result.value().entry->id);
maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/baz"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<uint16_t>(2), maybe_result.value().entry->id);
+ EXPECT_EQ(make_value<ResourceId>(0x01040002), maybe_result.value().entry->id);
}
-TEST(IdAssignerTest, FailWhenNonUniqueIdsAssigned) {
- std::unique_ptr<ResourceTable> table =
- test::ResourceTableBuilder()
- .AddSimple("android:attr/foo", ResourceId(0x01040006))
- .AddSimple("android:attr/bar", ResourceId(0x01040006))
- .SetPackageId("android", 0x01)
- .SetPackageId("app", 0x7f)
- .Build();
-
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+TEST_F(IdAssignerTests, FailWhenNonUniqueIdsAssigned) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo", ResourceId(0x01040006))
+ .AddSimple("android:attr/bar", ResourceId(0x01040006))
+ .Build();
IdAssigner assigner;
-
ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
}
-TEST(IdAssignerTest, AssignIdsWithIdMap) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .AddSimple("android:attr/foo")
- .AddSimple("android:attr/bar")
- .SetPackageId("android", 0x01)
- .Build();
+TEST_F(IdAssignerTests, FailWhenNonUniqueTypeIdsAssigned) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:string/foo", ResourceId(0x01040000))
+ .AddSimple("android:attr/bar", ResourceId(0x01040006))
+ .Build();
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIds) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo", ResourceId(0x01050000))
+ .AddSimple("android:attr/bar", ResourceId(0x01040006))
+ .Build();
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
+TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIdsRegardlessOfStagedId) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo", ResourceId(0x01050000))
+ .AddSimple("android:attr/bar", ResourceId(0x01ff0006))
+ .Add(NewResourceBuilder("android:attr/staged_baz")
+ .SetId(0x01ff0000)
+ .SetVisibility({.staged_api = true})
+ .Build())
+ .Build();
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
+TEST_F(IdAssignerTests, AssignIdsWithIdMap) {
+ auto table = test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo")
+ .AddSimple("android:attr/bar")
+ .Build();
std::unordered_map<ResourceName, ResourceId> id_map = {
{test::ParseNameOrDie("android:attr/foo"), ResourceId(0x01010002)}};
IdAssigner assigner(&id_map);
ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
ASSERT_TRUE(VerifyIds(table.get()));
- Maybe<ResourceTable::SearchResult> result =
- table->FindResource(test::ParseNameOrDie("android:attr/foo"));
+ auto result = table->FindResource(test::ParseNameOrDie("android:attr/foo"));
ASSERT_TRUE(result);
const ResourceTable::SearchResult& search_result = result.value();
- EXPECT_EQ(make_value<uint8_t>(0x01), search_result.package->id);
- EXPECT_EQ(make_value<uint8_t>(0x01), search_result.type->id);
- EXPECT_EQ(make_value<uint16_t>(0x0002), search_result.entry->id);
+ EXPECT_EQ(make_value<ResourceId>(0x01010002), search_result.entry->id);
+}
+
+TEST_F(IdAssignerTests, UseAllEntryIds) {
+ ResourceTable table;
+ const size_t max_entry_id = std::numeric_limits<uint16_t>::max();
+ for (size_t i = 0; i <= max_entry_id; i++) {
+ ASSERT_TRUE(
+ table.AddResource(NewResourceBuilder("android:attr/res" + std::to_string(i)).Build(),
+ context->GetDiagnostics()));
+ }
+ IdAssigner assigner;
+ ASSERT_TRUE(assigner.Consume(context.get(), &table));
+}
+
+TEST_F(IdAssignerTests, ExaustEntryIds) {
+ ResourceTable table;
+ const size_t max_entry_id = std::numeric_limits<uint16_t>::max() + 1u;
+ for (size_t i = 0; i <= max_entry_id; i++) {
+ ASSERT_TRUE(
+ table.AddResource(NewResourceBuilder("android:attr/res" + std::to_string(i)).Build(),
+ context->GetDiagnostics()));
+ }
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), &table));
+}
+
+TEST_F(IdAssignerTests, ExaustEntryIdsLastIdIsPublic) {
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(NewResourceBuilder("android:attr/res").SetId(0x0101ffff).Build(),
+ context->GetDiagnostics()));
+ const size_t max_entry_id = std::numeric_limits<uint16_t>::max();
+ for (size_t i = 0; i <= max_entry_id; i++) {
+ ASSERT_TRUE(
+ table.AddResource(NewResourceBuilder("android:attr/res" + std::to_string(i)).Build(),
+ context->GetDiagnostics()));
+ }
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), &table));
}
::testing::AssertionResult VerifyIds(ResourceTable* table) {
- std::set<uint8_t> package_ids;
+ std::set<ResourceId> seen_ids;
for (auto& package : table->packages) {
- if (!package->id) {
- return ::testing::AssertionFailure() << "package " << package->name
- << " has no ID";
- }
-
- if (!package_ids.insert(package->id.value()).second) {
- return ::testing::AssertionFailure()
- << "package " << package->name << " has non-unique ID " << std::hex
- << (int)package->id.value() << std::dec;
- }
- }
-
- for (auto& package : table->packages) {
- std::set<uint8_t> type_ids;
for (auto& type : package->types) {
- if (!type->id) {
- return ::testing::AssertionFailure() << "type " << type->type
- << " of package " << package->name
- << " has no ID";
- }
-
- if (!type_ids.insert(type->id.value()).second) {
- return ::testing::AssertionFailure()
- << "type " << type->type << " of package " << package->name
- << " has non-unique ID " << std::hex << (int)type->id.value()
- << std::dec;
- }
- }
-
- for (auto& type : package->types) {
- std::set<uint16_t> entry_ids;
for (auto& entry : type->entries) {
if (!entry->id) {
return ::testing::AssertionFailure()
- << "entry " << entry->name << " of type " << type->type
- << " of package " << package->name << " has no ID";
+ << "resource " << ResourceNameRef(package->name, type->type, entry->name)
+ << " has no ID";
}
-
- if (!entry_ids.insert(entry->id.value()).second) {
+ if (!seen_ids.insert(entry->id.value()).second) {
return ::testing::AssertionFailure()
- << "entry " << entry->name << " of type " << type->type
- << " of package " << package->name << " has non-unique ID "
- << std::hex << (int)entry->id.value() << std::dec;
+ << "resource " << ResourceNameRef(package->name, type->type, entry->name)
+ << " has a non-unique ID" << std::hex << entry->id.value() << std::dec;
}
}
}
}
+
return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
}
diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
index e816c86..432d7bf 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
@@ -236,13 +236,14 @@
TEST(PseudolocaleGeneratorTest, PluralsArePseudolocalized) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
- std::unique_ptr<ResourceTable> table =
- test::ResourceTableBuilder().SetPackageId("com.pkg", 0x7F).Build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder().Build();
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
plural->values = {util::make_unique<String>(table->string_pool.MakeRef("zero")),
util::make_unique<String>(table->string_pool.MakeRef("one"))};
- ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.pkg:plurals/foo"), ConfigDescription{},
- {}, std::move(plural), context->GetDiagnostics()));
+ ASSERT_TRUE(table->AddResource(NewResourceBuilder(test::ParseNameOrDie("com.pkg:plurals/foo"))
+ .SetValue(std::move(plural))
+ .Build(),
+ context->GetDiagnostics()));
std::unique_ptr<Plural> expected = util::make_unique<Plural>();
expected->values = {util::make_unique<String>(table->string_pool.MakeRef("[žéŕö one]")),
util::make_unique<String>(table->string_pool.MakeRef("[öñé one]"))};
@@ -252,6 +253,7 @@
const auto* actual = test::GetValueForConfig<Plural>(table.get(), "com.pkg:plurals/foo",
test::ParseConfigOrDie("en-rXA"));
+ ASSERT_NE(nullptr, actual);
EXPECT_TRUE(actual->Equals(expected.get()));
}
@@ -273,11 +275,14 @@
auto string = util::make_unique<String>(table->string_pool.MakeRef(original_style.str));
string->untranslatable_sections.push_back(UntranslatableSection{6u, 11u});
- ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("android:string/foo"), ConfigDescription{},
- {} /* product */, std::move(styled_string),
+ ASSERT_TRUE(table->AddResource(NewResourceBuilder(test::ParseNameOrDie("android:string/foo"))
+ .SetValue(std::move(styled_string))
+ .Build(),
context->GetDiagnostics()));
- ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("android:string/bar"), ConfigDescription{},
- {} /* product */, std::move(string), context->GetDiagnostics()));
+ ASSERT_TRUE(table->AddResource(NewResourceBuilder(test::ParseNameOrDie("android:string/bar"))
+ .SetValue(std::move(string))
+ .Build(),
+ context->GetDiagnostics()));
}
PseudolocaleGenerator generator;
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index cc1cf7f..f29c918 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -191,18 +191,14 @@
const ConfigDescription& config = DefaultConfig()) {
if (table) {
for (auto& package : table->packages) {
- if (package->id && package->id.value() == res_id.package_id()) {
for (auto& type : package->types) {
- if (type->id && type->id.value() == res_id.type_id()) {
- for (auto& entry : type->entries) {
- if (entry->id && entry->id.value() == res_id.entry_id()) {
- if (auto value = BestConfigValue(entry.get(), config)) {
- return value;
- }
+ for (auto& entry : type->entries) {
+ if (entry->id && entry->id.value() == res_id.id) {
+ if (auto value = BestConfigValue(entry.get(), config)) {
+ return value;
}
}
}
- }
}
}
}
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index cccd9fa..bfb8d58 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -192,8 +192,7 @@
std::u16string package_name = strcpy16_dtoh((const char16_t*)package_header->name,
arraysize(package_header->name));
- ResourceTablePackage* package =
- table_->CreatePackage(util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
+ ResourceTablePackage* package = table_->FindOrCreatePackage(util::Utf16ToUtf8(package_name));
if (!package) {
diag_->Error(DiagMessage(source_)
<< "incompatible package '" << package_name << "' with ID " << package_id);
@@ -232,13 +231,13 @@
break;
case android::RES_TABLE_TYPE_SPEC_TYPE:
- if (!ParseTypeSpec(package, parser.chunk())) {
+ if (!ParseTypeSpec(package, parser.chunk(), package_id)) {
return false;
}
break;
case android::RES_TABLE_TYPE_TYPE:
- if (!ParseType(package, parser.chunk())) {
+ if (!ParseType(package, parser.chunk(), package_id)) {
return false;
}
break;
@@ -276,7 +275,7 @@
}
bool BinaryResourceParser::ParseTypeSpec(const ResourceTablePackage* package,
- const ResChunk_header* chunk) {
+ const ResChunk_header* chunk, uint8_t package_id) {
if (type_pool_.getError() != NO_ERROR) {
diag_->Error(DiagMessage(source_) << "missing type string pool");
return false;
@@ -317,14 +316,14 @@
const uint32_t* type_spec_flags = reinterpret_cast<const uint32_t*>(
reinterpret_cast<uintptr_t>(type_spec) + util::DeviceToHost16(type_spec->header.headerSize));
for (size_t i = 0; i < entry_count; i++) {
- ResourceId id(package->id.value_or_default(0x0), type_spec->id, static_cast<size_t>(i));
+ ResourceId id(package_id, type_spec->id, static_cast<size_t>(i));
entry_type_spec_flags_[id] = util::DeviceToHost32(type_spec_flags[i]);
}
return true;
}
bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
- const ResChunk_header* chunk) {
+ const ResChunk_header* chunk, uint8_t package_id) {
if (type_pool_.getError() != NO_ERROR) {
diag_->Error(DiagMessage(source_) << "missing type string pool");
return false;
@@ -354,13 +353,9 @@
const std::string type_str = util::GetString(type_pool_, type->id - 1);
const ResourceType* parsed_type = ParseResourceType(type_str);
if (!parsed_type) {
- // Be lenient on the name of the type if the table is lenient on resource validation.
- bool log_error = table_->GetValidateResources();
- if (log_error) {
- diag_->Error(DiagMessage(source_) << "invalid type name '" << type_str
- << "' for type with ID " << type->id);
- }
- return !log_error;
+ diag_->Warn(DiagMessage(source_)
+ << "invalid type name '" << type_str << "' for type with ID " << type->id);
+ return true;
}
TypeVariant tv(type);
@@ -372,7 +367,7 @@
const ResourceName name(package->name, *parsed_type,
util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)));
- const ResourceId res_id(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
+ const ResourceId res_id(package_id, type->id, static_cast<uint16_t>(it.index()));
std::unique_ptr<Value> resource_value;
if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
@@ -392,17 +387,13 @@
return false;
}
- if (!table_->AddResourceWithIdMangled(name, res_id, config, {}, std::move(resource_value),
- diag_)) {
- return false;
- }
+ NewResourceBuilder res_builder(name);
+ res_builder.SetValue(std::move(resource_value), config)
+ .SetId(res_id, OnIdConflict::CREATE_ENTRY)
+ .SetAllowMangled(true);
if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
- Visibility visibility;
- visibility.level = Visibility::Level::kPublic;
- if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
- return false;
- }
+ res_builder.SetVisibility(Visibility{Visibility::Level::kPublic});
// Erase the ID from the map once processed, so that we don't mark the same symbol more than
// once.
@@ -415,6 +406,10 @@
if (cache_iter == id_index_.end()) {
id_index_.insert({res_id, name});
}
+
+ if (!table_->AddResource(res_builder.Build(), diag_)) {
+ return false;
+ }
}
return true;
}
@@ -472,7 +467,12 @@
OverlayableItem overlayable_item(overlayable);
overlayable_item.policies = policy_header->policy_flags;
- if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
+ if (!table_->AddResource(NewResourceBuilder(iter->second)
+ .SetId(res_id, OnIdConflict::CREATE_ENTRY)
+ .SetOverlayable(std::move(overlayable_item))
+ .SetAllowMangled(true)
+ .Build(),
+ diag_)) {
return false;
}
}
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index a2eee50..13dd982 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -51,8 +51,10 @@
bool ParseTable(const android::ResChunk_header* chunk);
bool ParsePackage(const android::ResChunk_header* chunk);
- bool ParseTypeSpec(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
- bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
+ bool ParseTypeSpec(const ResourceTablePackage* package, const android::ResChunk_header* chunk,
+ uint8_t package_id);
+ bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk,
+ uint8_t package_id);
bool ParseLibrary(const android::ResChunk_header* chunk);
bool ParseOverlayable(const android::ResChunk_header* chunk);
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 4b90b4f..17d11a6 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -59,35 +59,35 @@
dst[i] = 0;
}
-static bool cmp_style_entries(const Style::Entry& a, const Style::Entry& b) {
- if (a.key.id) {
- if (b.key.id) {
- return cmp_ids_dynamic_after_framework(a.key.id.value(), b.key.id.value());
+static bool cmp_style_entries(const Style::Entry* a, const Style::Entry* b) {
+ if (a->key.id) {
+ if (b->key.id) {
+ return cmp_ids_dynamic_after_framework(a->key.id.value(), b->key.id.value());
}
return true;
- } else if (!b.key.id) {
- return a.key.name.value() < b.key.name.value();
+ } else if (!b->key.id) {
+ return a->key.name.value() < b->key.name.value();
}
return false;
}
struct FlatEntry {
- ResourceEntry* entry;
- Value* value;
+ const ResourceEntry* entry;
+ const Value* value;
// The entry string pool index to the entry's name.
uint32_t entry_key;
};
-class MapFlattenVisitor : public ValueVisitor {
+class MapFlattenVisitor : public ConstValueVisitor {
public:
- using ValueVisitor::Visit;
+ using ConstValueVisitor::Visit;
MapFlattenVisitor(ResTable_entry_ext* out_entry, BigBuffer* buffer)
: out_entry_(out_entry), buffer_(buffer) {
}
- void Visit(Attribute* attr) override {
+ void Visit(const Attribute* attr) override {
{
Reference key = Reference(ResourceId(ResTable_map::ATTR_TYPE));
BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->type_mask);
@@ -106,13 +106,13 @@
FlattenEntry(&key, &val);
}
- for (Attribute::Symbol& s : attr->symbols) {
+ for (const Attribute::Symbol& s : attr->symbols) {
BinaryPrimitive val(s.type, s.value);
FlattenEntry(&s.symbol, &val);
}
}
- void Visit(Style* style) override {
+ void Visit(const Style* style) override {
if (style->parent) {
const Reference& parent_ref = style->parent.value();
CHECK(bool(parent_ref.id)) << "parent has no ID";
@@ -120,21 +120,26 @@
}
// Sort the style.
- std::sort(style->entries.begin(), style->entries.end(), cmp_style_entries);
+ std::vector<const Style::Entry*> sorted_entries;
+ for (const auto& entry : style->entries) {
+ sorted_entries.emplace_back(&entry);
+ }
- for (Style::Entry& entry : style->entries) {
- FlattenEntry(&entry.key, entry.value.get());
+ std::sort(sorted_entries.begin(), sorted_entries.end(), cmp_style_entries);
+
+ for (const Style::Entry* entry : sorted_entries) {
+ FlattenEntry(&entry->key, entry->value.get());
}
}
- void Visit(Styleable* styleable) override {
+ void Visit(const Styleable* styleable) override {
for (auto& attr_ref : styleable->entries) {
BinaryPrimitive val(Res_value{});
FlattenEntry(&attr_ref, &val);
}
}
- void Visit(Array* array) override {
+ void Visit(const Array* array) override {
const size_t count = array->elements.size();
for (size_t i = 0; i < count; i++) {
Reference key(android::ResTable_map::ATTR_MIN + i);
@@ -142,7 +147,7 @@
}
}
- void Visit(Plural* plural) override {
+ void Visit(const Plural* plural) override {
const size_t count = plural->values.size();
for (size_t i = 0; i < count; i++) {
if (!plural->values[i]) {
@@ -196,16 +201,16 @@
private:
DISALLOW_COPY_AND_ASSIGN(MapFlattenVisitor);
- void FlattenKey(Reference* key, ResTable_map* out_entry) {
+ void FlattenKey(const Reference* key, ResTable_map* out_entry) {
CHECK(bool(key->id)) << "key has no ID";
out_entry->name.ident = util::HostToDevice32(key->id.value().id);
}
- void FlattenValue(Item* value, ResTable_map* out_entry) {
+ void FlattenValue(const Item* value, ResTable_map* out_entry) {
CHECK(value->Flatten(&out_entry->value)) << "flatten failed";
}
- void FlattenEntry(Reference* key, Item* value) {
+ void FlattenEntry(const Reference* key, Item* value) {
ResTable_map* out_entry = buffer_->NextBlock<ResTable_map>();
FlattenKey(key, out_entry);
FlattenValue(value, out_entry);
@@ -226,7 +231,7 @@
class PackageFlattener {
public:
- PackageFlattener(IAaptContext* context, ResourceTablePackage* package,
+ PackageFlattener(IAaptContext* context, const ResourceTablePackageView& package,
const std::map<size_t, std::string>* shared_libs, bool use_sparse_entries,
bool collapse_key_stringpool,
const std::set<ResourceName>& name_collapse_exemptions)
@@ -243,20 +248,20 @@
TRACE_CALL();
ChunkWriter pkg_writer(buffer);
ResTable_package* pkg_header = pkg_writer.StartChunk<ResTable_package>(RES_TABLE_PACKAGE_TYPE);
- pkg_header->id = util::HostToDevice32(package_->id.value());
+ pkg_header->id = util::HostToDevice32(package_.id.value());
// AAPT truncated the package name, so do the same.
// Shared libraries require full package names, so don't truncate theirs.
if (context_->GetPackageType() != PackageType::kApp &&
- package_->name.size() >= arraysize(pkg_header->name)) {
- diag_->Error(DiagMessage() << "package name '" << package_->name
+ package_.name.size() >= arraysize(pkg_header->name)) {
+ diag_->Error(DiagMessage() << "package name '" << package_.name
<< "' is too long. "
"Shared libraries cannot have truncated package names");
return false;
}
// Copy the package name in device endianness.
- strcpy16_htod(pkg_header->name, arraysize(pkg_header->name), util::Utf8ToUtf16(package_->name));
+ strcpy16_htod(pkg_header->name, arraysize(pkg_header->name), util::Utf8ToUtf16(package_.name));
// Serialize the types. We do this now so that our type and key strings
// are populated. We write those first.
@@ -273,7 +278,7 @@
buffer->AppendBuffer(std::move(type_buffer));
// If there are libraries (or if the package ID is 0x00), encode a library chunk.
- if (package_->id.value() == 0x00 || !shared_libs_->empty()) {
+ if (package_.id.value() == 0x00 || !shared_libs_->empty()) {
FlattenLibrarySpec(buffer);
}
@@ -315,7 +320,7 @@
}
bool FlattenValue(FlatEntry* entry, BigBuffer* buffer) {
- if (Item* item = ValueCast<Item>(entry->value)) {
+ if (const Item* item = ValueCast<Item>(entry->value)) {
WriteEntry<ResTable_entry, true>(entry, buffer);
Res_value* outValue = buffer->NextBlock<Res_value>();
CHECK(item->Flatten(outValue)) << "flatten failed";
@@ -329,7 +334,7 @@
return true;
}
- bool FlattenConfig(const ResourceTableType* type, const ConfigDescription& config,
+ bool FlattenConfig(const ResourceTableTypeView& type, const ConfigDescription& config,
const size_t num_total_entries, std::vector<FlatEntry>* entries,
BigBuffer* buffer) {
CHECK(num_total_entries != 0);
@@ -337,7 +342,7 @@
ChunkWriter type_writer(buffer);
ResTable_type* type_header = type_writer.StartChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
- type_header->id = type->id.value();
+ type_header->id = type.id.value();
type_header->config = config;
type_header->config.swapHtoD();
@@ -346,12 +351,12 @@
BigBuffer values_buffer(512);
for (FlatEntry& flat_entry : *entries) {
- CHECK(static_cast<size_t>(flat_entry.entry->id.value()) < num_total_entries);
- offsets[flat_entry.entry->id.value()] = values_buffer.size();
+ CHECK(static_cast<size_t>(flat_entry.entry->id.value().entry_id()) < num_total_entries);
+ offsets[flat_entry.entry->id.value().entry_id()] = values_buffer.size();
if (!FlattenValue(&flat_entry, &values_buffer)) {
diag_->Error(DiagMessage()
<< "failed to flatten resource '"
- << ResourceNameRef(package_->name, type->type, flat_entry.entry->name)
+ << ResourceNameRef(package_.name, type.type, flat_entry.entry->name)
<< "' for configuration '" << config << "'");
return false;
}
@@ -399,55 +404,27 @@
return true;
}
- std::vector<ResourceTableType*> CollectAndSortTypes() {
- std::vector<ResourceTableType*> sorted_types;
- for (auto& type : package_->types) {
- if (type->type == ResourceType::kStyleable) {
- // Styleables aren't real Resource Types, they are represented in the
- // R.java file.
- continue;
- }
-
- CHECK(bool(type->id)) << "type must have an ID set";
-
- sorted_types.push_back(type.get());
- }
- std::sort(sorted_types.begin(), sorted_types.end(), cmp_ids<ResourceTableType>);
- return sorted_types;
- }
-
- std::vector<ResourceEntry*> CollectAndSortEntries(ResourceTableType* type) {
- // Sort the entries by entry ID.
- std::vector<ResourceEntry*> sorted_entries;
- for (auto& entry : type->entries) {
- CHECK(bool(entry->id)) << "entry must have an ID set";
- sorted_entries.push_back(entry.get());
- }
- std::sort(sorted_entries.begin(), sorted_entries.end(), cmp_ids<ResourceEntry>);
- return sorted_entries;
- }
-
bool FlattenOverlayable(BigBuffer* buffer) {
std::set<ResourceId> seen_ids;
std::map<std::string, OverlayableChunk> overlayable_chunks;
- CHECK(bool(package_->id)) << "package must have an ID set when flattening <overlayable>";
- for (auto& type : package_->types) {
- CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
- for (auto& entry : type->entries) {
- CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
+ CHECK(bool(package_.id)) << "package must have an ID set when flattening <overlayable>";
+ for (auto& type : package_.types) {
+ CHECK(bool(type.id)) << "type must have an ID set when flattening <overlayable>";
+ for (auto& entry : type.entries) {
+ CHECK(bool(type.id)) << "entry must have an ID set when flattening <overlayable>";
if (!entry->overlayable_item) {
continue;
}
- OverlayableItem& item = entry->overlayable_item.value();
+ const OverlayableItem& item = entry->overlayable_item.value();
// Resource ids should only appear once in the resource table
- ResourceId id = android::make_resid(package_->id.value(), type->id.value(),
- entry->id.value());
+ ResourceId id =
+ android::make_resid(package_.id.value(), type.id.value(), entry->id.value().entry_id());
CHECK(seen_ids.find(id) == seen_ids.end())
<< "multiple overlayable definitions found for resource "
- << ResourceName(package_->name, type->type, entry->name).to_string();
+ << ResourceName(package_.name, type.type, entry->name).to_string();
seen_ids.insert(id);
// Find the overlayable chunk with the specified name
@@ -542,14 +519,14 @@
return true;
}
- bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,
- BigBuffer* buffer) {
+ bool FlattenTypeSpec(const ResourceTableTypeView& type,
+ const std::vector<const ResourceEntry*>& sorted_entries, BigBuffer* buffer) {
ChunkWriter type_spec_writer(buffer);
ResTable_typeSpec* spec_header =
type_spec_writer.StartChunk<ResTable_typeSpec>(RES_TABLE_TYPE_SPEC_TYPE);
- spec_header->id = type->id.value();
+ spec_header->id = type.id.value();
- if (sorted_entries->empty()) {
+ if (sorted_entries.empty()) {
type_spec_writer.Finish();
return true;
}
@@ -557,7 +534,7 @@
// We can't just take the size of the vector. There may be holes in the
// entry ID space.
// Since the entries are sorted by ID, the last one will be the biggest.
- const size_t num_entries = sorted_entries->back()->id.value() + 1;
+ const size_t num_entries = sorted_entries.back()->id.value().entry_id() + 1;
spec_header->entryCount = util::HostToDevice32(num_entries);
@@ -565,21 +542,23 @@
// show for which configuration axis the resource changes.
uint32_t* config_masks = type_spec_writer.NextBlock<uint32_t>(num_entries);
- const size_t actual_num_entries = sorted_entries->size();
- for (size_t entryIndex = 0; entryIndex < actual_num_entries; entryIndex++) {
- ResourceEntry* entry = sorted_entries->at(entryIndex);
+ for (const ResourceEntry* entry : sorted_entries) {
+ const uint16_t entry_id = entry->id.value().entry_id();
// Populate the config masks for this entry.
+ uint32_t& entry_config_masks = config_masks[entry_id];
if (entry->visibility.level == Visibility::Level::kPublic) {
- config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ }
+ if (entry->visibility.staged_api) {
+ entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_STAGED_API);
}
const size_t config_count = entry->values.size();
for (size_t i = 0; i < config_count; i++) {
const ConfigDescription& config = entry->values[i]->config;
for (size_t j = i + 1; j < config_count; j++) {
- config_masks[entry->id.value()] |=
- util::HostToDevice32(config.diff(entry->values[j]->config));
+ config_masks[entry_id] |= util::HostToDevice32(config.diff(entry->values[j]->config));
}
}
}
@@ -590,32 +569,31 @@
bool FlattenTypes(BigBuffer* buffer) {
// Sort the types by their IDs. They will be inserted into the StringPool in
// this order.
- std::vector<ResourceTableType*> sorted_types = CollectAndSortTypes();
size_t expected_type_id = 1;
- for (ResourceTableType* type : sorted_types) {
+ for (const ResourceTableTypeView& type : package_.types) {
+ if (type.type == ResourceType::kStyleable) {
+ // Styleables aren't real Resource Types, they are represented in the R.java file.
+ continue;
+ }
+
// If there is a gap in the type IDs, fill in the StringPool
// with empty values until we reach the ID we expect.
- while (type->id.value() > expected_type_id) {
+ while (type.id.value() > expected_type_id) {
std::stringstream type_name;
type_name << "?" << expected_type_id;
type_pool_.MakeRef(type_name.str());
expected_type_id++;
}
expected_type_id++;
- type_pool_.MakeRef(to_string(type->type));
+ type_pool_.MakeRef(to_string(type.type));
- std::vector<ResourceEntry*> sorted_entries = CollectAndSortEntries(type);
- if (sorted_entries.empty()) {
- continue;
- }
-
- if (!FlattenTypeSpec(type, &sorted_entries, buffer)) {
+ if (!FlattenTypeSpec(type, type.entries, buffer)) {
return false;
}
// Since the entries are sorted by ID, the last ID will be the largest.
- const size_t num_entries = sorted_entries.back()->id.value() + 1;
+ const size_t num_entries = type.entries.back()->id.value().entry_id() + 1;
// The binary resource table lists resource entries for each
// configuration.
@@ -628,9 +606,9 @@
// hardcoded string uses characters which make it an invalid resource name
const std::string obfuscated_resource_name = "0_resource_name_obfuscated";
- for (ResourceEntry* entry : sorted_entries) {
+ for (const ResourceEntry* entry : type.entries) {
uint32_t local_key_index;
- ResourceName resource_name({}, type->type, entry->name);
+ ResourceName resource_name({}, type.type, entry->name);
if (!collapse_key_stringpool_ ||
name_collapse_exemptions_.find(resource_name) != name_collapse_exemptions_.end()) {
local_key_index = (uint32_t)key_pool_.MakeRef(entry->name).index();
@@ -660,17 +638,17 @@
ResTable_lib_header* lib_header =
lib_writer.StartChunk<ResTable_lib_header>(RES_TABLE_LIBRARY_TYPE);
- const size_t num_entries = (package_->id.value() == 0x00 ? 1 : 0) + shared_libs_->size();
+ const size_t num_entries = (package_.id.value() == 0x00 ? 1 : 0) + shared_libs_->size();
CHECK(num_entries > 0);
lib_header->count = util::HostToDevice32(num_entries);
ResTable_lib_entry* lib_entry = buffer->NextBlock<ResTable_lib_entry>(num_entries);
- if (package_->id.value() == 0x00) {
+ if (package_.id.value() == 0x00) {
// Add this package
lib_entry->packageId = util::HostToDevice32(0x00);
strcpy16_htod(lib_entry->packageName, arraysize(lib_entry->packageName),
- util::Utf8ToUtf16(package_->name));
+ util::Utf8ToUtf16(package_.name));
++lib_entry;
}
@@ -685,7 +663,7 @@
IAaptContext* context_;
IDiagnostics* diag_;
- ResourceTablePackage* package_;
+ const ResourceTablePackageView package_;
const std::map<size_t, std::string>* shared_libs_;
bool use_sparse_entries_;
StringPool type_pool_;
@@ -709,9 +687,10 @@
});
// Write the ResTable header.
+ const auto& table_view = table->GetPartitionedView();
ChunkWriter table_writer(buffer_);
ResTable_header* table_header = table_writer.StartChunk<ResTable_header>(RES_TABLE_TYPE);
- table_header->packageCount = util::HostToDevice32(table->packages.size());
+ table_header->packageCount = util::HostToDevice32(table_view.packages.size());
// Flatten the values string pool.
StringPool::FlattenUtf8(table_writer.buffer(), table->string_pool,
@@ -720,24 +699,25 @@
BigBuffer package_buffer(1024);
// Flatten each package.
- for (auto& package : table->packages) {
+ for (auto& package : table_view.packages) {
if (context->GetPackageType() == PackageType::kApp) {
// Write a self mapping entry for this package if the ID is non-standard (0x7f).
- const uint8_t package_id = package->id.value();
+ CHECK((bool)package.id) << "Resource ids have not been assigned before flattening the table";
+ const uint8_t package_id = package.id.value();
if (package_id != kFrameworkPackageId && package_id != kAppPackageId) {
- auto result = table->included_packages_.insert({package_id, package->name});
- if (!result.second && result.first->second != package->name) {
+ auto result = table->included_packages_.insert({package_id, package.name});
+ if (!result.second && result.first->second != package.name) {
// A mapping for this package ID already exists, and is a different package. Error!
context->GetDiagnostics()->Error(
DiagMessage() << android::base::StringPrintf(
"can't map package ID %02x to '%s'. Already mapped to '%s'", package_id,
- package->name.c_str(), result.first->second.c_str()));
+ package.name.c_str(), result.first->second.c_str()));
return false;
}
}
}
- PackageFlattener flattener(context, package.get(), &table->included_packages_,
+ PackageFlattener flattener(context, package, &table->included_packages_,
options_.use_sparse_entries, options_.collapse_key_stringpool,
options_.name_collapse_exemptions);
if (!flattener.FlattenPackage(&package_buffer)) {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index f8b8a1c..f5fe5e3 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -155,7 +155,6 @@
TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
.AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
.AddValue("com.app.test:id/three", ResourceId(0x7f020002),
@@ -204,7 +203,6 @@
TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddSimple("com.app.test:id/one", ResourceId(0x7f020001))
.AddSimple("com.app.test:id/three", ResourceId(0x7f020003))
.Build();
@@ -225,7 +223,6 @@
attr.max_int = 23;
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>(attr))
.Build();
@@ -248,7 +245,6 @@
2u));
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddValue("android:array/foo", ResourceId(0x01010000), std::move(array))
.Build();
@@ -300,7 +296,6 @@
IAaptContext* context, const ConfigDescription& sparse_config, float load) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId(context->GetCompilationPackage(), context->GetPackageId())
.Build();
// Add regular entries.
@@ -311,15 +306,20 @@
const ResourceId resid(context->GetPackageId(), 0x02, static_cast<uint16_t>(i));
const auto value =
util::make_unique<BinaryPrimitive>(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(i));
- CHECK(table->AddResourceWithId(name, resid, ConfigDescription::DefaultConfig(), "",
- std::unique_ptr<Value>(value->Clone(nullptr)),
- context->GetDiagnostics()));
+ CHECK(table->AddResource(NewResourceBuilder(name)
+ .SetId(resid)
+ .SetValue(std::unique_ptr<Value>(value->Clone(nullptr)))
+ .Build(),
+ context->GetDiagnostics()));
// Every few entries, write out a sparse_config value. This will give us the desired load.
if (i % stride == 0) {
- CHECK(table->AddResourceWithId(name, resid, sparse_config, "",
- std::unique_ptr<Value>(value->Clone(nullptr)),
- context->GetDiagnostics()));
+ CHECK(table->AddResource(
+ NewResourceBuilder(name)
+ .SetId(resid)
+ .SetValue(std::unique_ptr<Value>(value->Clone(nullptr)), sparse_config)
+ .Build(),
+ context->GetDiagnostics()));
}
}
return table;
@@ -417,7 +417,6 @@
test::ContextBuilder().SetCompilationPackage("lib").SetPackageId(0x00).Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("lib", 0x00)
.AddValue("lib:id/foo", ResourceId(0x00010000), util::make_unique<Id>())
.Build();
ResourceTable result;
@@ -426,7 +425,7 @@
Maybe<ResourceTable::SearchResult> search_result =
result.FindResource(test::ParseNameOrDie("lib:id/foo"));
ASSERT_TRUE(search_result);
- EXPECT_EQ(0x00u, search_result.value().package->id.value());
+ EXPECT_EQ(0x00u, search_result.value().entry->id.value().package_id());
auto iter = result.included_packages_.find(0x00);
ASSERT_NE(result.included_packages_.end(), iter);
@@ -438,7 +437,6 @@
test::ContextBuilder().SetCompilationPackage("lib").SetPackageId(0x00).Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("lib", 0x00)
.AddValue("lib:style/Theme",
ResourceId(0x00030001),
test::StyleBuilder()
@@ -458,9 +456,7 @@
Maybe<ResourceTable::SearchResult> search_result =
result.FindResource(test::ParseNameOrDie("lib:style/Theme"));
ASSERT_TRUE(search_result);
- EXPECT_EQ(0x00u, search_result.value().package->id.value());
- EXPECT_EQ(0x03u, search_result.value().type->id.value());
- EXPECT_EQ(0x01u, search_result.value().entry->id.value());
+ EXPECT_EQ(0x00030001u, search_result.value().entry->id.value());
ASSERT_EQ(1u, search_result.value().entry->values.size());
Value* value = search_result.value().entry->values[0]->value.get();
Style* style = ValueCast<Style>(value);
@@ -479,7 +475,6 @@
test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x7f).Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("app", 0x7f)
.AddValue("app:id/foo", ResourceId(0x7f010000),
test::BuildReference("lib_one:id/foo", ResourceId(0x02010000)))
.AddValue("app:id/bar", ResourceId(0x7f010001),
@@ -509,7 +504,6 @@
std::unique_ptr<IAaptContext> context =
test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x80).Build();
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .SetPackageId("app", 0x80)
.AddSimple("app:id/foo", ResourceId(0x80010000))
.Build();
@@ -532,7 +526,6 @@
test::ContextBuilder().SetCompilationPackage(kPackageName).SetPackageId(0x7f).Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId(kPackageName, 0x7f)
.AddSimple(kPackageName + ":id/foo", ResourceId(0x7f010000))
.Build();
@@ -553,7 +546,6 @@
.Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId(kPackageName, 0x7f)
.AddSimple(kPackageName + ":id/foo", ResourceId(0x7f010000))
.Build();
@@ -564,7 +556,6 @@
TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoNameCollapseExemptionsSucceeds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
.AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
.AddValue("com.app.test:id/three", ResourceId(0x7f020002),
@@ -618,7 +609,6 @@
TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
.AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
.AddValue("com.app.test:id/three", ResourceId(0x7f020002),
@@ -680,7 +670,6 @@
std::string name = "com.app.test:integer/overlayable";
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddSimple(name, ResourceId(0x7f020000))
.SetOverlayable(name, overlayable_item)
.Build();
@@ -717,7 +706,6 @@
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddSimple(name_zero, ResourceId(0x7f020000))
.SetOverlayable(name_zero, overlayable_item_zero)
.AddSimple(name_one, ResourceId(0x7f020001))
@@ -780,7 +768,6 @@
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddSimple(name_zero, ResourceId(0x7f020000))
.SetOverlayable(name_zero, overlayable_item_zero)
.AddSimple(name_one, ResourceId(0x7f020001))
@@ -842,7 +829,6 @@
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddSimple(name_zero, ResourceId(0x7f020000))
.SetOverlayable(name_zero, overlayable_item_zero)
.Build();
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 06ac9e5..498d5a2 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -425,15 +425,9 @@
io::IFileCollection* files,
const std::vector<std::shared_ptr<Overlayable>>& overlayables,
ResourceTable* out_table, std::string* out_error) {
- Maybe<uint8_t> id;
- if (pb_package.has_package_id()) {
- id = static_cast<uint8_t>(pb_package.package_id().id());
- }
-
std::map<ResourceId, ResourceNameRef> id_index;
- ResourceTablePackage* pkg =
- out_table->CreatePackageAllowingDuplicateNames(pb_package.package_name(), id);
+ ResourceTablePackage* pkg = out_table->FindOrCreatePackage(pb_package.package_name());
for (const pb::Type& pb_type : pb_package.type()) {
const ResourceType* res_type = ParseResourceType(pb_type.name());
if (res_type == nullptr) {
@@ -444,17 +438,15 @@
}
ResourceTableType* type = pkg->FindOrCreateType(*res_type);
- if (pb_type.has_type_id()) {
- type->id = static_cast<uint8_t>(pb_type.type_id().id());
- }
for (const pb::Entry& pb_entry : pb_type.entry()) {
- ResourceEntry* entry;
- if (pb_entry.has_entry_id()) {
- auto entry_id = static_cast<uint16_t>(pb_entry.entry_id().id());
- entry = type->FindOrCreateEntry(pb_entry.name(), entry_id);
- } else {
- entry = type->FindOrCreateEntry(pb_entry.name());
+ ResourceEntry* entry = type->CreateEntry(pb_entry.name());
+ const ResourceId resource_id(
+ pb_package.has_package_id() ? static_cast<uint8_t>(pb_package.package_id().id()) : 0u,
+ pb_type.has_type_id() ? static_cast<uint8_t>(pb_type.type_id().id()) : 0u,
+ pb_entry.has_entry_id() ? static_cast<uint16_t>(pb_entry.entry_id().id()) : 0u);
+ if (resource_id.id != 0u) {
+ entry->id = resource_id;
}
// Deserialize the symbol status (public/private with source and comments).
@@ -464,6 +456,7 @@
DeserializeSourceFromPb(pb_visibility.source(), src_pool, &entry->visibility.source);
}
entry->visibility.comment = pb_visibility.comment();
+ entry->visibility.staged_api = pb_visibility.staged_api();
const Visibility::Level level = DeserializeVisibilityFromPb(pb_visibility.level());
entry->visibility.level = level;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 98c5175..f13f82d 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -345,28 +345,29 @@
pb_fingerprint->set_version(util::GetToolFingerprint());
std::vector<Overlayable*> overlayables;
- for (const std::unique_ptr<ResourceTablePackage>& package : table.packages) {
+ auto table_view = table.GetPartitionedView();
+ for (const auto& package : table_view.packages) {
pb::Package* pb_package = out_table->add_package();
- if (package->id) {
- pb_package->mutable_package_id()->set_id(package->id.value());
+ if (package.id) {
+ pb_package->mutable_package_id()->set_id(package.id.value());
}
- pb_package->set_package_name(package->name);
+ pb_package->set_package_name(package.name);
- for (const std::unique_ptr<ResourceTableType>& type : package->types) {
+ for (const auto& type : package.types) {
pb::Type* pb_type = pb_package->add_type();
- if (type->id) {
- pb_type->mutable_type_id()->set_id(type->id.value());
+ if (type.id) {
+ pb_type->mutable_type_id()->set_id(type.id.value());
}
- pb_type->set_name(to_string(type->type).to_string());
+ pb_type->set_name(to_string(type.type).to_string());
// hardcoded string uses characters which make it an invalid resource name
static const char* obfuscated_resource_name = "0_resource_name_obfuscated";
- for (const std::unique_ptr<ResourceEntry>& entry : type->entries) {
+ for (const auto& entry : type.entries) {
pb::Entry* pb_entry = pb_type->add_entry();
if (entry->id) {
- pb_entry->mutable_entry_id()->set_id(entry->id.value());
+ pb_entry->mutable_entry_id()->set_id(entry->id.value().entry_id());
}
- ResourceName resource_name({}, type->type, entry->name);
+ ResourceName resource_name({}, type.type, entry->name);
if (options.collapse_key_stringpool &&
options.name_collapse_exemptions.find(resource_name) ==
options.name_collapse_exemptions.end()) {
@@ -377,6 +378,7 @@
// Write the Visibility struct.
pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
+ pb_visibility->set_staged_api(entry->visibility.staged_api);
pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
if (source_pool != nullptr) {
SerializeSourceToPb(entry->visibility.source, source_pool.get(),
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index fe4c8aa..591ba149 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -40,18 +40,67 @@
MOCK_METHOD0(GetDirSeparator, char());
};
-ResourceEntry* GetEntry(ResourceTable* table, const ResourceNameRef& res_name,
- uint32_t id) {
- ResourceTablePackage* package = table->FindPackage(res_name.package);
- ResourceTableType* type = package->FindType(res_name.type);
- return type->FindEntry(res_name.entry, id);
+ResourceEntry* GetEntry(ResourceTable* table, const ResourceNameRef& res_name) {
+ auto result = table->FindResource(res_name);
+ return (result) ? result.value().entry : nullptr;
+}
+
+ResourceEntry* GetEntry(ResourceTable* table, const ResourceNameRef& res_name, ResourceId id) {
+ auto result = table->FindResource(res_name, id);
+ return (result) ? result.value().entry : nullptr;
+}
+
+TEST(ProtoSerializeTest, SerializeVisibility) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .Add(NewResourceBuilder("com.app.a:bool/foo")
+ .SetVisibility({Visibility::Level::kUndefined})
+ .Build())
+ .Add(NewResourceBuilder("com.app.a:bool/bar")
+ .SetVisibility({Visibility::Level::kPrivate})
+ .Build())
+ .Add(NewResourceBuilder("com.app.a:bool/baz")
+ .SetVisibility({Visibility::Level::kPublic})
+ .Build())
+ .Add(NewResourceBuilder("com.app.a:bool/fiz")
+ .SetVisibility({.level = Visibility::Level::kPublic, .staged_api = true})
+ .Build())
+ .Build();
+
+ ResourceTable new_table;
+ pb::ResourceTable pb_table;
+ MockFileCollection files;
+ std::string error;
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ auto search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+ EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPrivate));
+ EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
+ ASSERT_TRUE(search_result);
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_TRUE(search_result.value().entry->visibility.staged_api);
}
TEST(ProtoSerializeTest, SerializeSinglePackage) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.AddFileReference("com.app.a:layout/main", ResourceId(0x7f020000), "res/layout/main.xml")
.AddReference("com.app.a:layout/other", ResourceId(0x7f020001), "com.app.a:layout/main")
.AddString("com.app.a:string/text", {}, "hi")
@@ -60,11 +109,11 @@
true /*allow_new*/)
.Build();
- Visibility public_symbol;
- public_symbol.level = Visibility::Level::kPublic;
- ASSERT_TRUE(table->SetVisibilityWithId(test::ParseNameOrDie("com.app.a:layout/main"),
- public_symbol, ResourceId(0x7f020000),
- context->GetDiagnostics()));
+ ASSERT_TRUE(table->AddResource(NewResourceBuilder(test::ParseNameOrDie("com.app.a:layout/main"))
+ .SetId(0x7f020000)
+ .SetVisibility({Visibility::Level::kPublic})
+ .Build(),
+ context->GetDiagnostics()));
Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo");
ASSERT_THAT(id, NotNull());
@@ -72,25 +121,35 @@
// Make a plural.
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
plural->values[Plural::One] = util::make_unique<String>(table->string_pool.MakeRef("one"));
- ASSERT_TRUE(table->AddResource(test::ParseNameOrDie("com.app.a:plurals/hey"), ConfigDescription{},
- {}, std::move(plural), context->GetDiagnostics()));
+ ASSERT_TRUE(table->AddResource(NewResourceBuilder(test::ParseNameOrDie("com.app.a:plurals/hey"))
+ .SetValue(std::move(plural))
+ .Build(),
+ context->GetDiagnostics()));
// Make a styled string.
StyleString style_string;
style_string.str = "hello";
style_string.spans.push_back(Span{"b", 0u, 4u});
- ASSERT_TRUE(
- table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {},
- util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)),
- context->GetDiagnostics()));
+ ASSERT_TRUE(table->AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("com.app.a:string/styled"))
+ .SetValue(util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)))
+ .Build(),
+ context->GetDiagnostics()));
// Make a resource with different products.
- ASSERT_TRUE(table->AddResource(
- test::ParseNameOrDie("com.app.a:integer/one"), test::ParseConfigOrDie("land"), {},
- test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 123u), context->GetDiagnostics()));
- ASSERT_TRUE(table->AddResource(
- test::ParseNameOrDie("com.app.a:integer/one"), test::ParseConfigOrDie("land"), "tablet",
- test::BuildPrimitive(android::Res_value::TYPE_INT_HEX, 321u), context->GetDiagnostics()));
+ ASSERT_TRUE(
+ table->AddResource(NewResourceBuilder(test::ParseNameOrDie("com.app.a:integer/one"))
+ .SetValue(test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 123u),
+ test::ParseConfigOrDie("land"))
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(
+ table->AddResource(NewResourceBuilder(test::ParseNameOrDie("com.app.a:integer/one"))
+ .SetValue(test::BuildPrimitive(android::Res_value::TYPE_INT_HEX, 321u),
+ test::ParseConfigOrDie("land"), "tablet")
+ .Build(),
+ context->GetDiagnostics()));
// Make a reference with both resource name and resource ID.
// The reference should point to a resource outside of this table to test that both name and id
@@ -98,16 +157,20 @@
Reference expected_ref;
expected_ref.name = test::ParseNameOrDie("android:layout/main");
expected_ref.id = ResourceId(0x01020000);
- ASSERT_TRUE(table->AddResource(
- test::ParseNameOrDie("com.app.a:layout/abc"), ConfigDescription::DefaultConfig(), {},
- util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
+ ASSERT_TRUE(table->AddResource(NewResourceBuilder(test::ParseNameOrDie("com.app.a:layout/abc"))
+ .SetValue(util::make_unique<Reference>(expected_ref))
+ .Build(),
+ context->GetDiagnostics()));
// Make an overlayable resource.
OverlayableItem overlayable_item(std::make_shared<Overlayable>(
"OverlayableName", "overlay://theme", Source("res/values/overlayable.xml", 40)));
overlayable_item.source = Source("res/values/overlayable.xml", 42);
- ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
- overlayable_item, test::GetDiagnostics()));
+ ASSERT_TRUE(
+ table->AddResource(NewResourceBuilder(test::ParseNameOrDie("com.app.a:integer/overlayable"))
+ .SetOverlayable(overlayable_item)
+ .Build(),
+ context->GetDiagnostics()));
pb::ResourceTable pb_table;
SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
@@ -680,7 +743,6 @@
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddSimple("com.app.test:id/one", ResourceId(id_one_id))
.AddSimple("com.app.test:id/two", ResourceId(id_two_id))
.AddValue("com.app.test:id/three", ResourceId(id_three_id),
@@ -714,7 +776,7 @@
ResourceName real_id_resource(
"com.app.test", ResourceType::kId, "one");
- EXPECT_THAT(GetEntry(&new_table, real_id_resource, id_one_id), IsNull());
+ EXPECT_THAT(GetEntry(&new_table, real_id_resource), IsNull());
ResourceName obfuscated_id_resource(
"com.app.test", ResourceType::kId, "0_resource_name_obfuscated");
@@ -767,7 +829,6 @@
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddSimple("com.app.test:id/one", ResourceId(id_one_id))
.AddSimple("com.app.test:id/two", ResourceId(id_two_id))
.AddValue("com.app.test:id/three", ResourceId(id_three_id),
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 995495a..d3648c8 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -59,8 +59,9 @@
template <typename T>
class PrimitiveMember : public ClassMember {
public:
- PrimitiveMember(const android::StringPiece& name, const T& val)
- : name_(name.to_string()), val_(val) {}
+ PrimitiveMember(const android::StringPiece& name, const T& val, bool staged_api = false)
+ : name_(name.to_string()), val_(val), staged_api_(staged_api) {
+ }
bool empty() const override {
return false;
@@ -77,7 +78,7 @@
ClassMember::Print(final, printer, strip_api_annotations);
printer->Print("public static ");
- if (final) {
+ if (final && !staged_api_) {
printer->Print("final ");
}
printer->Print("int ").Print(name_).Print("=").Print(to_string(val_)).Print(";");
@@ -88,14 +89,16 @@
std::string name_;
T val_;
+ bool staged_api_;
};
// Specialization for strings so they get the right type and are quoted with "".
template <>
class PrimitiveMember<std::string> : public ClassMember {
public:
- PrimitiveMember(const android::StringPiece& name, const std::string& val)
- : name_(name.to_string()), val_(val) {}
+ PrimitiveMember(const android::StringPiece& name, const std::string& val, bool staged_api = false)
+ : name_(name.to_string()), val_(val) {
+ }
bool empty() const override {
return false;
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 59dd481..e1e2e01 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -460,7 +460,8 @@
const std::string field_name = TransformToFieldName(name.entry);
if (out_class_def != nullptr) {
- auto resource_member = util::make_unique<ResourceMember>(field_name, real_id);
+ auto resource_member =
+ util::make_unique<ResourceMember>(field_name, real_id, entry.visibility.staged_api);
// Build the comments and annotations for this entry.
AnnotationProcessor* processor = resource_member->GetCommentBuilder();
@@ -542,8 +543,8 @@
// Create an ID if there is one (static libraries don't need one).
ResourceId id;
- if (package.id && type.id && entry->id) {
- id = ResourceId(package.id.value(), type.id.value(), entry->id.value());
+ if (entry->id) {
+ id = entry->id.value();
}
// We need to make sure we hide the fact that we are generating kAttrPrivate attributes.
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index ec5b415..8bb3ee9 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -34,7 +34,6 @@
TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddSimple("android:id/class", ResourceId(0x01020000))
.Build();
@@ -54,7 +53,6 @@
TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddSimple("android:id/hey-man", ResourceId(0x01020000))
.AddValue("android:attr/cool.attr", ResourceId(0x01010000),
test::AttributeBuilder().Build())
@@ -84,7 +82,6 @@
TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddSimple("android:id/one", ResourceId(0x01020000))
.AddSimple("android:id/com.foo$two", ResourceId(0x01020001))
.Build();
@@ -110,8 +107,6 @@
TEST(JavaClassGeneratorTest, StyleableAttributesWithDifferentPackageName) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
- .SetPackageId("app", 0x7f)
.AddValue("app:attr/foo", ResourceId(0x7f010000),
test::AttributeBuilder().Build())
.AddValue("app:attr/bar", ResourceId(0x7f010001),
@@ -159,7 +154,6 @@
TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddSimple("android:attr/two", ResourceId(0x01010001))
.AddSimple("android:^attr-private/one", ResourceId(0x01010000))
.Build();
@@ -184,7 +178,6 @@
StdErrDiagnostics diag;
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddSimple("android:id/one", ResourceId(0x01020000))
.AddSimple("android:id/two", ResourceId(0x01020001))
.AddSimple("android:id/three", ResourceId(0x01020002))
@@ -276,8 +269,6 @@
TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
- .SetPackageId("com.lib", 0x02)
.AddValue("android:attr/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
.AddValue("com.lib:attr/bar", ResourceId(0x02010000), test::AttributeBuilder().Build())
.AddValue("android:styleable/foo", ResourceId(0x01030000),
@@ -306,7 +297,6 @@
TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddSimple("android:id/foo", ResourceId(0x01010000))
.Build();
test::GetValue<Id>(table.get(), "android:id/foo")
@@ -346,7 +336,6 @@
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddValue("android:attr/one", util::make_unique<Attribute>(attr))
.AddValue("android:styleable/Container",
std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
@@ -384,7 +373,6 @@
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddValue("android:attr/one", util::make_unique<Attribute>(attr))
.AddValue("android:styleable/Container",
std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
@@ -415,7 +403,6 @@
TEST(JavaClassGeneratorTest, StyleableAndIndicesAreColocated) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddValue("android:attr/layout_gravity", util::make_unique<Attribute>())
.AddValue("android:attr/background", util::make_unique<Attribute>())
.AddValue("android:styleable/ActionBar",
@@ -467,7 +454,6 @@
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddValue("android:attr/one", util::make_unique<Attribute>(attr))
.Build();
@@ -499,7 +485,6 @@
TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x00)
.AddValue("android:attr/foo", ResourceId(0x00010000), util::make_unique<Attribute>())
.AddValue("android:id/foo", ResourceId(0x00020000), util::make_unique<Id>())
.AddValue(
@@ -536,7 +521,6 @@
TEST(JavaClassGeneratorTest, OnlyGenerateRText) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>())
.AddValue("android:styleable/hey.dude", ResourceId(0x01020000),
test::StyleableBuilder()
@@ -554,8 +538,6 @@
TEST(JavaClassGeneratorTest, SortsDynamicAttributesAfterFrameworkAttributes) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
- .SetPackageId("lib", 0x00)
.AddValue("android:attr/framework_attr", ResourceId(0x01010000),
test::AttributeBuilder().Build())
.AddValue("lib:attr/dynamic_attr", ResourceId(0x00010000),
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index c7ae0b6..b7dfec3 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -247,8 +247,10 @@
ResourceTable table;
StdErrDiagnostics errDiagnostics;
- table.AddResource(bar_layout->file.name, ConfigDescription::DefaultConfig(), "",
- util::make_unique<FileReference>(), &errDiagnostics);
+ table.AddResource(NewResourceBuilder(bar_layout->file.name)
+ .SetValue(util::make_unique<FileReference>())
+ .Build(),
+ &errDiagnostics);
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 1117472..02fd00b 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -54,7 +54,6 @@
TEST(AutoVersionerTest, VersionStylesForTable) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("app", 0x7f)
.AddValue(
"app:style/Foo", test::ParseConfigOrDie("v4"),
ResourceId(0x7f020000),
diff --git a/tools/aapt2/link/NoDefaultResourceRemover_test.cpp b/tools/aapt2/link/NoDefaultResourceRemover_test.cpp
index d129c9a..3179fef 100644
--- a/tools/aapt2/link/NoDefaultResourceRemover_test.cpp
+++ b/tools/aapt2/link/NoDefaultResourceRemover_test.cpp
@@ -21,10 +21,9 @@
namespace aapt {
TEST(NoDefaultResourceRemoverTest, RemoveEntryWithNoDefaultAndOnlyLocales) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().SetPackageId(0x01).Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddSimple("android:string/foo")
.AddSimple("android:string/foo", test::ParseConfigOrDie("en-rGB"))
.AddSimple("android:string/foo", test::ParseConfigOrDie("fr-rFR"))
@@ -47,10 +46,10 @@
}
TEST(NoDefaultResourceRemoverTest, KeepEntryWithLocalesAndDensities) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().SetMinSdkVersion(26).Build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetPackageId(0x01).SetMinSdkVersion(26).Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddSimple("android:drawable/keep1", test::ParseConfigOrDie("mdpi")) // v4
.AddSimple("android:drawable/keep1", test::ParseConfigOrDie("en-rGB"))
.AddSimple("android:drawable/keep1", test::ParseConfigOrDie("fr-rFR"))
@@ -71,10 +70,10 @@
}
TEST(NoDefaultResourceRemoverTest, RemoveEntryWithLocalesAndDensitiesLowVersion) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().SetMinSdkVersion(3).Build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetPackageId(0x01).SetMinSdkVersion(3).Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddSimple("android:drawable/remove1", test::ParseConfigOrDie("mdpi")) // v4
.AddSimple("android:drawable/remove1", test::ParseConfigOrDie("en-rGB"))
.AddSimple("android:drawable/remove1", test::ParseConfigOrDie("fr-rFR"))
@@ -87,10 +86,10 @@
}
TEST(NoDefaultResourceRemoverTest, KeepEntryWithVersion) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().SetMinSdkVersion(8).Build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetPackageId(0x01).SetMinSdkVersion(8).Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("android", 0x01)
.AddSimple("android:drawable/keep1", test::ParseConfigOrDie("v8"))
.AddSimple("android:drawable/keep1", test::ParseConfigOrDie("en-rGB"))
.AddSimple("android:drawable/keep1", test::ParseConfigOrDie("fr-rFR"))
diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/link/ProductFilter_test.cpp
index dd47674..4f78bbc 100644
--- a/tools/aapt2/link/ProductFilter_test.cpp
+++ b/tools/aapt2/link/ProductFilter_test.cpp
@@ -30,21 +30,29 @@
ResourceTable table;
ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"), land, "",
- test::ValueBuilder<Id>().SetSource(Source("land/default.xml")).Build(),
- context->GetDiagnostics()));
- ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"), land, "tablet",
- test::ValueBuilder<Id>().SetSource(Source("land/tablet.xml")).Build(),
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source("land/default.xml")).Build(), land)
+ .Build(),
context->GetDiagnostics()));
ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"), port, "",
- test::ValueBuilder<Id>().SetSource(Source("port/default.xml")).Build(),
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source("land/tablet.xml")).Build(), land,
+ "tablet")
+ .Build(),
context->GetDiagnostics()));
+
ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"), port, "tablet",
- test::ValueBuilder<Id>().SetSource(Source("port/tablet.xml")).Build(),
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source("port/default.xml")).Build(), port)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source("port/tablet.xml")).Build(), port,
+ "tablet")
+ .Build(),
context->GetDiagnostics()));
ProductFilter filter({"tablet"});
@@ -65,15 +73,17 @@
ResourceTable table;
ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"),
- ConfigDescription::DefaultConfig(), "",
- test::ValueBuilder<Id>().SetSource(Source("default.xml")).Build(),
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source("default.xml")).Build())
+ .Build(),
context->GetDiagnostics()));
+
ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"),
- ConfigDescription::DefaultConfig(), "tablet",
- test::ValueBuilder<Id>().SetSource(Source("tablet.xml")).Build(),
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source("tablet.xml")).Build(), {}, "tablet")
+ .Build(),
context->GetDiagnostics()));
+ ;
ProductFilter filter(std::unordered_set<std::string>{});
ASSERT_TRUE(filter.Consume(context.get(), &table));
@@ -91,19 +101,22 @@
ResourceTable table;
ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"),
- ConfigDescription::DefaultConfig(), "",
- test::ValueBuilder<Id>().SetSource(Source("default.xml")).Build(),
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source("default.xml")).Build())
+ .Build(),
context->GetDiagnostics()));
+
ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"),
- ConfigDescription::DefaultConfig(), "tablet",
- test::ValueBuilder<Id>().SetSource(Source("tablet.xml")).Build(),
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source("tablet.xml")).Build(), {}, "tablet")
+ .Build(),
context->GetDiagnostics()));
+
ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"),
- ConfigDescription::DefaultConfig(), "no-sdcard",
- test::ValueBuilder<Id>().SetSource(Source("no-sdcard.xml")).Build(),
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source("no-sdcard.xml")).Build(), {},
+ "no-sdcard")
+ .Build(),
context->GetDiagnostics()));
ProductFilter filter({"tablet", "no-sdcard"});
@@ -114,15 +127,17 @@
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
ResourceTable table;
+ ASSERT_TRUE(
+ table.AddResource(NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source(".xml")).Build())
+ .Build(),
+ context->GetDiagnostics()));
+
ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"),
- ConfigDescription::DefaultConfig(), "",
- test::ValueBuilder<Id>().SetSource(Source(".xml")).Build(),
- context->GetDiagnostics()));
- ASSERT_TRUE(table.AddResource(
- test::ParseNameOrDie("android:string/one"),
- ConfigDescription::DefaultConfig(), "default",
- test::ValueBuilder<Id>().SetSource(Source("default.xml")).Build(),
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(Source("default.xml")).Build(), {},
+ "default")
+ .Build(),
context->GetDiagnostics()));
ProductFilter filter(std::unordered_set<std::string>{});
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index a31ce94..228c5bd74 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -28,7 +28,6 @@
TEST(ReferenceLinkerTest, LinkSimpleReferences) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddReference("com.app.test:string/foo", ResourceId(0x7f020000),
"com.app.test:string/bar")
@@ -75,7 +74,6 @@
TEST(ReferenceLinkerTest, LinkStyleAttributes) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddValue("com.app.test:style/Theme",
test::StyleBuilder()
.SetParent("android:style/Theme.Material")
@@ -155,7 +153,6 @@
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddValue("com.app.test:style/Theme", ResourceId(0x7f020000),
test::StyleBuilder()
.AddItem("com.android.support:attr/foo",
@@ -176,7 +173,6 @@
TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddReference("com.app.test:string/foo", ResourceId(0x7f020000),
"android:string/hidden")
.Build();
@@ -201,7 +197,6 @@
TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddReference("com.app.test:string/foo", ResourceId(0x7f020000),
"com.app.lib:string/hidden")
.Build();
@@ -229,7 +224,6 @@
TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .SetPackageId("com.app.test", 0x7f)
.AddValue("com.app.test:style/Theme",
test::StyleBuilder()
.AddItem("android:attr/hidden",
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index ad56092..4ef2882 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -33,8 +33,7 @@
const TableMergerOptions& options)
: context_(context), main_table_(out_table), options_(options) {
// Create the desired package that all tables will be merged into.
- main_package_ =
- main_table_->CreatePackage(context_->GetCompilationPackage(), context_->GetPackageId());
+ main_package_ = main_table_->FindOrCreatePackage(context_->GetCompilationPackage());
CHECK(main_package_ != nullptr) << "package name or ID already taken";
}
@@ -85,20 +84,9 @@
static bool MergeType(IAaptContext* context, const Source& src, ResourceTableType* dst_type,
ResourceTableType* src_type) {
- if (src_type->visibility_level > dst_type->visibility_level) {
+ if (src_type->visibility_level >= dst_type->visibility_level) {
// The incoming type's visibility is stronger, so we should override the visibility.
- if (src_type->visibility_level == Visibility::Level::kPublic) {
- // Only copy the ID if the source is public, or else the ID is meaningless.
- dst_type->id = src_type->id;
- }
dst_type->visibility_level = src_type->visibility_level;
- } else if (dst_type->visibility_level == Visibility::Level::kPublic &&
- src_type->visibility_level == Visibility::Level::kPublic && dst_type->id &&
- src_type->id && dst_type->id.value() != src_type->id.value()) {
- // Both types are public and have different IDs.
- context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge type '" << src_type->type
- << "': conflicting public IDs");
- return false;
}
return true;
}
@@ -347,7 +335,7 @@
file_ref->type = file_desc.type;
file_ref->file = file;
- ResourceTablePackage* pkg = table.CreatePackage(file_desc.name.package, 0x0);
+ ResourceTablePackage* pkg = table.FindOrCreatePackage(file_desc.name.package);
pkg->FindOrCreateType(file_desc.name.type)
->FindOrCreateEntry(file_desc.name.entry)
->FindOrCreateValue(file_desc.config, {})
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 69cf5ee..4358fb5 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -55,16 +55,13 @@
TEST_F(TableMergerTest, SimpleMerge) {
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.AddReference("com.app.a:id/foo", "com.app.a:id/bar")
.AddReference("com.app.a:id/bar", "com.app.b:id/foo")
- .AddValue(
- "com.app.a:styleable/view",
- test::StyleableBuilder().AddItem("com.app.b:id/foo").Build())
+ .AddValue("com.app.a:styleable/view",
+ test::StyleableBuilder().AddItem("com.app.b:id/foo").Build())
.Build();
std::unique_ptr<ResourceTable> table_b = test::ResourceTableBuilder()
- .SetPackageId("com.app.b", 0x7f)
.AddSimple("com.app.b:id/foo")
.Build();
@@ -129,12 +126,10 @@
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.AddFileReference("com.app.a:xml/file", "res/xml/file.xml", &file_a)
.Build();
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("com.app.b", 0x7f)
.AddFileReference("com.app.b:xml/file", "res/xml/file.xml", &file_b)
.Build();
@@ -158,12 +153,10 @@
TEST_F(TableMergerTest, OverrideResourceWithOverlay) {
std::unique_ptr<ResourceTable> base =
test::ResourceTableBuilder()
- .SetPackageId("", 0x00)
.AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
.Build();
std::unique_ptr<ResourceTable> overlay =
test::ResourceTableBuilder()
- .SetPackageId("", 0x00)
.AddValue("bool/foo", ResourceUtils::TryParseBool("false"))
.Build();
@@ -194,14 +187,12 @@
std::unique_ptr<ResourceTable> base =
test::ResourceTableBuilder()
- .SetPackageId("", 0x00)
.AddValue("bool/foo", std::move(foo_original))
.AddValue("bool/bar", std::move(bar_original))
.Build();
std::unique_ptr<ResourceTable> overlay =
test::ResourceTableBuilder()
- .SetPackageId("", 0x00)
.AddValue("bool/foo", std::move(foo_overlay))
.AddValue("bool/bar", std::move(bar_overlay))
.AddValue("bool/baz", std::move(baz_overlay))
@@ -226,12 +217,10 @@
TEST_F(TableMergerTest, OverrideSameResourceIdsWithOverlay) {
std::unique_ptr<ResourceTable> base =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
.Build();
std::unique_ptr<ResourceTable> overlay =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
.Build();
@@ -247,12 +236,10 @@
TEST_F(TableMergerTest, FailToOverrideConflictingTypeIdsWithOverlay) {
std::unique_ptr<ResourceTable> base =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
.Build();
std::unique_ptr<ResourceTable> overlay =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.SetSymbolState("bool/foo", ResourceId(0x7f, 0x02, 0x0001), Visibility::Level::kPublic)
.Build();
@@ -268,12 +255,10 @@
TEST_F(TableMergerTest, FailToOverrideConflictingEntryIdsWithOverlay) {
std::unique_ptr<ResourceTable> base =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
.Build();
std::unique_ptr<ResourceTable> overlay =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0002), Visibility::Level::kPublic)
.Build();
@@ -289,12 +274,10 @@
TEST_F(TableMergerTest, FailConflictingVisibility) {
std::unique_ptr<ResourceTable> base =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
.Build();
std::unique_ptr<ResourceTable> overlay =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPrivate)
.Build();
@@ -318,11 +301,9 @@
}
TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
- std::unique_ptr<ResourceTable> table_a =
- test::ResourceTableBuilder().SetPackageId("", 0x7f).Build();
+ std::unique_ptr<ResourceTable> table_a = test::ResourceTableBuilder().Build();
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.SetSymbolState("bool/foo", {}, Visibility::Level::kUndefined, true /*allow new overlay*/)
.AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
.Build();
@@ -337,11 +318,9 @@
}
TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
- std::unique_ptr<ResourceTable> table_a =
- test::ResourceTableBuilder().SetPackageId("", 0x7f).Build();
+ std::unique_ptr<ResourceTable> table_a = test::ResourceTableBuilder().Build();
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
.Build();
@@ -355,11 +334,9 @@
}
TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
- std::unique_ptr<ResourceTable> table_a =
- test::ResourceTableBuilder().SetPackageId("", 0x7f).Build();
+ std::unique_ptr<ResourceTable> table_a = test::ResourceTableBuilder().Build();
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
.AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
.Build();
@@ -375,7 +352,6 @@
TEST_F(TableMergerTest, OverlaidStyleablesAndStylesShouldBeMerged) {
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.AddValue("com.app.a:styleable/Foo",
test::StyleableBuilder()
.AddItem("com.app.a:attr/bar")
@@ -391,7 +367,6 @@
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.AddValue("com.app.a:styleable/Foo", test::StyleableBuilder()
.AddItem("com.app.a:attr/bat")
.AddItem("com.app.a:attr/foo")
@@ -441,7 +416,6 @@
TEST_F(TableMergerTest, OverrideStyleInsteadOfOverlaying) {
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.AddValue(
"com.app.a:styleable/MyWidget",
test::StyleableBuilder().AddItem("com.app.a:attr/foo", ResourceId(0x1234)).Build())
@@ -452,7 +426,6 @@
.Build();
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.AddValue(
"com.app.a:styleable/MyWidget",
test::StyleableBuilder().AddItem("com.app.a:attr/bar", ResourceId(0x5678)).Build())
@@ -494,13 +467,11 @@
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.SetOverlayable("bool/foo", overlayable_item)
.Build();
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.AddSimple("bool/foo")
.Build();
@@ -527,7 +498,6 @@
"overlay://customization");
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.AddSimple("bool/foo")
.Build();
@@ -536,7 +506,6 @@
overlayable_item.policies |= PolicyFlags::SYSTEM_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.SetOverlayable("bool/foo", overlayable_item)
.Build();
@@ -565,7 +534,6 @@
overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.SetOverlayable("bool/foo", overlayable_item_first)
.Build();
@@ -575,7 +543,6 @@
overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.SetOverlayable("bool/foo", overlayable_item_second)
.Build();
@@ -594,7 +561,6 @@
overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.SetOverlayable("bool/foo", overlayable_item_first)
.Build();
@@ -604,7 +570,6 @@
overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.SetOverlayable("bool/foo", overlayable_item_second)
.Build();
@@ -623,7 +588,6 @@
overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.SetOverlayable("bool/foo", overlayable_item_first)
.Build();
@@ -633,7 +597,6 @@
overlayable_item_second.policies |= PolicyFlags::SIGNATURE;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.SetOverlayable("bool/foo", overlayable_item_second)
.Build();
@@ -653,7 +616,6 @@
overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.SetOverlayable("bool/foo", overlayable_item_first)
.Build();
@@ -661,7 +623,6 @@
overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
- .SetPackageId("com.app.a", 0x7f)
.SetOverlayable("bool/foo", overlayable_item_second)
.Build();
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 98ee63d..d385267 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -197,9 +197,10 @@
std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>();
symbol->is_public = (sr.entry->visibility.level == Visibility::Level::kPublic);
- if (sr.package->id && sr.type->id && sr.entry->id) {
- symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
- symbol->is_dynamic = (sr.package->id.value() == 0);
+ if (sr.entry->id) {
+ symbol->id = sr.entry->id.value();
+ symbol->is_dynamic =
+ (sr.entry->id.value().package_id() == 0) || sr.entry->visibility.staged_api;
}
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
@@ -374,7 +375,8 @@
if (s) {
s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package);
+ s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package) ||
+ (type_spec_flags & android::ResTable_typeSpec::SPEC_STAGED_API) != 0;
return s;
}
return {};
@@ -421,7 +423,8 @@
if (s) {
s->is_public = (*flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package);
+ s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package) ||
+ (*flags & android::ResTable_typeSpec::SPEC_STAGED_API) != 0;
return s;
}
return {};
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 6a67271..2f319b1 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -185,7 +185,7 @@
// Initialize all packages for splits.
for (size_t idx = 0; idx < split_count; idx++) {
ResourceTable* split_table = splits_[idx].get();
- split_table->CreatePackage(pkg->name, pkg->id);
+ split_table->FindOrCreatePackage(pkg->name);
}
for (auto& type : pkg->types) {
@@ -241,10 +241,7 @@
// not have actual values for each type/entry.
ResourceTablePackage* split_pkg = split_table->FindPackage(pkg->name);
ResourceTableType* split_type = split_pkg->FindOrCreateType(type->type);
- if (!split_type->id) {
- split_type->id = type->id;
- split_type->visibility_level = type->visibility_level;
- }
+ split_type->visibility_level = type->visibility_level;
ResourceEntry* split_entry = split_type->FindOrCreateEntry(entry->name);
if (!split_entry->id) {
diff --git a/tools/aapt2/split/TableSplitter_test.cpp b/tools/aapt2/split/TableSplitter_test.cpp
index cdf0738..c6a2ff3 100644
--- a/tools/aapt2/split/TableSplitter_test.cpp
+++ b/tools/aapt2/split/TableSplitter_test.cpp
@@ -193,15 +193,23 @@
ResourceTable table;
const ResourceName foo = test::ParseNameOrDie("android:string/foo");
- ASSERT_TRUE(table.AddResource(foo, test::ParseConfigOrDie("land-hdpi"), {},
- util::make_unique<Id>(),
- test::GetDiagnostics()));
- ASSERT_TRUE(table.AddResource(foo, test::ParseConfigOrDie("land-xhdpi"), {},
- util::make_unique<Id>(),
- test::GetDiagnostics()));
- ASSERT_TRUE(table.AddResource(foo, test::ParseConfigOrDie("land-xxhdpi"), {},
- util::make_unique<Id>(),
- test::GetDiagnostics()));
+ ASSERT_TRUE(
+ table.AddResource(NewResourceBuilder(foo)
+ .SetValue(util::make_unique<Id>(), test::ParseConfigOrDie("land-hdpi"))
+ .Build(),
+ test::GetDiagnostics()));
+
+ ASSERT_TRUE(
+ table.AddResource(NewResourceBuilder(foo)
+ .SetValue(util::make_unique<Id>(), test::ParseConfigOrDie("land-xhdpi"))
+ .Build(),
+ test::GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(foo)
+ .SetValue(util::make_unique<Id>(), test::ParseConfigOrDie("land-xxhdpi"))
+ .Build(),
+ test::GetDiagnostics()));
std::vector<SplitConstraints> constraints;
constraints.push_back(
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 9a93f2a..4816596 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -34,13 +34,6 @@
namespace aapt {
namespace test {
-ResourceTableBuilder& ResourceTableBuilder::SetPackageId(const StringPiece& package_name,
- uint8_t id) {
- ResourceTablePackage* package = table_->CreatePackage(package_name, id);
- CHECK(package != nullptr);
- return *this;
-}
-
ResourceTableBuilder& ResourceTableBuilder::AddSimple(const StringPiece& name,
const ResourceId& id) {
return AddValue(name, id, util::make_unique<Id>());
@@ -118,8 +111,13 @@
const ResourceId& id,
std::unique_ptr<Value> value) {
ResourceName res_name = ParseNameOrDie(name);
- CHECK(table_->AddResourceWithIdMangled(res_name, id, config, {}, std::move(value),
- GetDiagnostics()));
+ NewResourceBuilder builder(res_name);
+ builder.SetValue(std::move(value), config).SetAllowMangled(true);
+ if (id.id != 0U) {
+ builder.SetId(id);
+ }
+
+ CHECK(table_->AddResource(builder.Build(), GetDiagnostics()));
return *this;
}
@@ -128,10 +126,13 @@
Visibility::Level level,
bool allow_new) {
ResourceName res_name = ParseNameOrDie(name);
- Visibility visibility;
- visibility.level = level;
- CHECK(table_->SetVisibilityWithIdMangled(res_name, visibility, id, GetDiagnostics()));
- CHECK(table_->SetAllowNewMangled(res_name, AllowNew{}, GetDiagnostics()));
+ NewResourceBuilder builder(res_name);
+ builder.SetVisibility({level}).SetAllowNew({}).SetAllowMangled(true);
+ if (id.id != 0U) {
+ builder.SetId(id);
+ }
+
+ CHECK(table_->AddResource(builder.Build(), GetDiagnostics()));
return *this;
}
@@ -139,7 +140,14 @@
const OverlayableItem& overlayable) {
ResourceName res_name = ParseNameOrDie(name);
- CHECK(table_->SetOverlayable(res_name, overlayable, GetDiagnostics()));
+ CHECK(table_->AddResource(
+ NewResourceBuilder(res_name).SetOverlayable(overlayable).SetAllowMangled(true).Build(),
+ GetDiagnostics()));
+ return *this;
+}
+
+ResourceTableBuilder& ResourceTableBuilder::Add(NewResource&& res) {
+ CHECK(table_->AddResource(std::move(res), GetDiagnostics()));
return *this;
}
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index c971a1b..3ff955d 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -39,7 +39,6 @@
public:
ResourceTableBuilder() = default;
- ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id);
ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {});
ResourceTableBuilder& AddSimple(const android::StringPiece& name,
const android::ConfigDescription& config,
@@ -75,6 +74,7 @@
Visibility::Level level, bool allow_new = false);
ResourceTableBuilder& SetOverlayable(const android::StringPiece& name,
const OverlayableItem& overlayable);
+ ResourceTableBuilder& Add(NewResource&& res);
StringPool* string_pool();
std::unique_ptr<ResourceTable> Build();