diff --git a/Android.bp b/Android.bp
index 557c3209248daaf7504ae788cd6d9a074198e99b..14a2bff8ad13c1fbbe4b72dd85fe709c3f988b19 100644
--- a/Android.bp
+++ b/Android.bp
@@ -264,6 +264,7 @@ filegroup {
":libcamera_client_aidl",
":libcamera_client_framework_aidl",
":libupdate_engine_aidl",
+ ":resourcemanager_aidl",
":storaged_aidl",
":vold_aidl",
@@ -292,13 +293,13 @@ filegroup {
java_library {
name: "framework-updatable-stubs-module_libs_api",
static_libs: [
- "framework-media-stubs-module_libs_api",
- "framework-mediaprovider-stubs-module_libs_api",
- "framework-permission-stubs-module_libs_api",
- "framework-sdkextensions-stubs-module_libs_api",
- "framework-statsd-stubs-module_libs_api",
- "framework-tethering-stubs-module_libs_api",
- "framework-wifi-stubs-module_libs_api",
+ "framework-media.stubs.module_lib",
+ "framework-mediaprovider.stubs.module_lib",
+ "framework-permission.stubs.module_lib",
+ "framework-sdkextensions.stubs.module_lib",
+ "framework-statsd.stubs.module_lib",
+ "framework-tethering.stubs.module_lib",
+ "framework-wifi.stubs.module_lib",
],
sdk_version: "module_current",
visibility: [":__pkg__"],
@@ -533,6 +534,8 @@ java_library {
static_libs: [
"exoplayer2-extractor",
"android.hardware.wifi-V1.0-java-constants",
+ // Additional dependencies needed to build the ike API classes.
+ "ike-internals",
],
apex_available: ["//apex_available:platform"],
visibility: [
@@ -700,7 +703,6 @@ filegroup {
"core/java/com/android/internal/util/TrafficStatsConstants.java",
"core/java/com/android/internal/util/WakeupMessage.java",
"core/java/com/android/internal/util/TokenBucket.java",
- "core/java/android/net/shared/*.java",
],
}
@@ -708,7 +710,6 @@ filegroup {
name: "framework-services-net-module-wifi-shared-srcs",
srcs: [
"core/java/android/net/DhcpResults.java",
- "core/java/android/net/shared/InetAddressUtils.java",
"core/java/android/net/util/IpUtils.java",
"core/java/android/util/LocalLog.java",
],
@@ -725,7 +726,6 @@ filegroup {
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
"core/java/com/android/internal/util/TrafficStatsConstants.java",
- "core/java/android/net/shared/Inet4AddressUtils.java",
],
}
@@ -1162,7 +1162,6 @@ java_library {
srcs: [
"core/java/android/content/pm/BaseParceledListSlice.java",
"core/java/android/content/pm/ParceledListSlice.java",
- "core/java/android/net/shared/Inet4AddressUtils.java",
"core/java/android/os/HandlerExecutor.java",
"core/java/com/android/internal/util/AsyncChannel.java",
"core/java/com/android/internal/util/AsyncService.java",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index c0197c4b2acfb36fa157c5b13a41f76662d6df72..ef4e202fdfe25f6752c192978647c0e600ee134c 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -329,13 +329,13 @@ java_library_static {
srcs: [ ":api-stubs-docs-non-updatable" ],
static_libs: [
"conscrypt.module.public.api.stubs",
- "framework-media-stubs-publicapi",
- "framework-mediaprovider-stubs-publicapi",
- "framework-permission-stubs-publicapi",
- "framework-sdkextensions-stubs-publicapi",
- "framework-statsd-stubs-publicapi",
- "framework-tethering-stubs-publicapi",
- "framework-wifi-stubs-publicapi",
+ "framework-media.stubs",
+ "framework-mediaprovider.stubs",
+ "framework-permission.stubs",
+ "framework-sdkextensions.stubs",
+ "framework-statsd.stubs",
+ "framework-tethering.stubs",
+ "framework-wifi.stubs",
"private-stub-annotations-jar",
],
defaults: ["android_defaults_stubs_current"],
@@ -359,13 +359,13 @@ java_library_static {
srcs: [ ":system-api-stubs-docs-non-updatable" ],
static_libs: [
"conscrypt.module.public.api.stubs",
- "framework-media-stubs-systemapi",
- "framework-mediaprovider-stubs-systemapi",
- "framework-permission-stubs-systemapi",
- "framework-sdkextensions-stubs-systemapi",
- "framework-statsd-stubs-systemapi",
- "framework-tethering-stubs-systemapi",
- "framework-wifi-stubs-systemapi",
+ "framework-media.stubs.system",
+ "framework-mediaprovider.stubs.system",
+ "framework-permission.stubs.system",
+ "framework-sdkextensions.stubs.system",
+ "framework-statsd.stubs.system",
+ "framework-tethering.stubs.system",
+ "framework-wifi.stubs.system",
"private-stub-annotations-jar",
],
defaults: ["android_defaults_stubs_current"],
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 508bf6007a9f75869e67b9677b39866aa1e64b9a..04432f25e33706b65fa74951cab654ac94ac0824 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -17,6 +17,7 @@ android_test {
srcs: ["src/**/*.java"],
static_libs: [
"androidx.test.rules",
+ "collector-device-lib-platform",
"apct-perftests-utils",
],
platform_apis: true,
diff --git a/apct-tests/perftests/multiuser/AndroidManifest.xml b/apct-tests/perftests/multiuser/AndroidManifest.xml
index 893c8ca9328ba145d15ed0d830e61860360d288a..e4196dd6cf12fdcf8396aac18067ff38ebf3f51b 100644
--- a/apct-tests/perftests/multiuser/AndroidManifest.xml
+++ b/apct-tests/perftests/multiuser/AndroidManifest.xml
@@ -17,12 +17,16 @@
+
+
+
+
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
index 1667c1658a0704adca47d236ec86b1d2b4b11a05..6122ef254855fcb0858794583d0e20dd1171879e 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
@@ -23,6 +23,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static org.hamcrest.core.AnyOf.anyOf;
import static org.hamcrest.core.Is.is;
+import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
@@ -121,6 +122,12 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
@AfterClass
public static void tearDownClass() {
sSetUpClassException = null;
+ try {
+ // Recents activity may stop app switches. Restore the state to avoid affecting
+ // the next test.
+ ActivityManager.resumeAppSwitches();
+ } catch (RemoteException ignored) {
+ }
sUiAutomation.dropShellPermissionIdentity();
}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index 8139a2e963c561d1c169caa3c11b16211f48c03c..f04e5556752047f95d43debb4f1d36a680f8380f 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -88,10 +88,7 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase {
public void testRelayout() throws Throwable {
final Activity activity = mActivityRule.getActivity();
final ContentView contentView = new ContentView(activity);
- mActivityRule.runOnUiThread(() -> {
- activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- activity.setContentView(contentView);
- });
+ mActivityRule.runOnUiThread(() -> activity.setContentView(contentView));
getInstrumentation().waitForIdleSync();
final RelayoutRunner relayoutRunner = new RelayoutRunner(activity, contentView.getWindow(),
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
index 9e17e940a06bf6a4cb1314f4ec325b5a81e2b06b..655d2f7f8aa767b896d1c7ce88b97864b4209e94 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
@@ -19,11 +19,13 @@ package android.wm;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.app.Activity;
+import android.app.KeyguardManager;
import android.app.UiAutomation;
import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;
import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
import android.perftests.utils.PerfTestActivity;
import android.provider.Settings;
@@ -61,24 +63,32 @@ public class WindowManagerPerfTestBase {
@BeforeClass
public static void setUpOnce() {
final Context context = getInstrumentation().getContext();
- sOriginalStayOnWhilePluggedIn = Settings.Global.getInt(context.getContentResolver(),
+ final int stayOnWhilePluggedIn = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
- // Keep the device awake during testing.
- setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_USB);
+ sOriginalStayOnWhilePluggedIn = -1;
+ if (stayOnWhilePluggedIn != BatteryManager.BATTERY_PLUGGED_ANY) {
+ sOriginalStayOnWhilePluggedIn = stayOnWhilePluggedIn;
+ // Keep the device awake during testing.
+ setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_ANY);
+ }
if (!BASE_OUT_PATH.exists()) {
executeShellCommand("mkdir -p " + BASE_OUT_PATH);
}
- // In order to be closer to the real use case.
- executeShellCommand("input keyevent KEYCODE_WAKEUP");
- executeShellCommand("wm dismiss-keyguard");
+ if (!context.getSystemService(PowerManager.class).isInteractive()
+ || context.getSystemService(KeyguardManager.class).isKeyguardLocked()) {
+ executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ executeShellCommand("wm dismiss-keyguard");
+ }
context.startActivity(new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
@AfterClass
public static void tearDownOnce() {
- setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
+ if (sOriginalStayOnWhilePluggedIn != -1) {
+ setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
+ }
}
private static void setStayOnWhilePluggedIn(int value) {
diff --git a/apex/Android.bp b/apex/Android.bp
index c1715a002d6d19375e42cc74b1b0b33c9315f851..992648b04ef04c5f0d5110642c0a6e327c37759e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -63,9 +63,9 @@ mainline_service_stubs_args =
"--hide-annotation android.annotation.Hide " +
"--hide InternalClasses " // com.android.* classes are okay in this interface
-// Defaults for mainline module provided java_sdk_library instances.
+// Defaults common to all mainline module java_sdk_library instances.
java_defaults {
- name: "framework-module-defaults",
+ name: "framework-module-common-defaults",
// Additional annotations used for compiling both the implementation and the
// stubs libraries.
@@ -88,24 +88,14 @@ java_defaults {
enabled: true,
sdk_version: "module_current",
},
- system: {
- enabled: true,
- sdk_version: "module_current",
- },
- module_lib: {
- enabled: true,
- sdk_version: "module_current",
- },
// Configure framework module specific metalava options.
droiddoc_options: [mainline_stubs_args],
annotations_enabled: true,
- // The stub libraries must be visible to frameworks/base so they can be combined
- // into API specific libraries.
stubs_library_visibility: [
- "//frameworks/base", // Framework
+ "//visibility:public",
],
// Set the visibility of the modules creating the stubs source.
@@ -127,6 +117,32 @@ java_defaults {
sdk_version: "module_current",
}
+// Defaults for mainline module provided java_sdk_library instances.
+java_defaults {
+ name: "framework-module-defaults",
+ defaults: ["framework-module-common-defaults"],
+
+ system: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+ module_lib: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+}
+
+// Defaults for mainline module system server provided java_sdk_library instances.
+java_defaults {
+ name: "framework-system-server-module-defaults",
+ defaults: ["framework-module-common-defaults"],
+
+ system_server: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+}
+
stubs_defaults {
name: "framework-module-stubs-defaults-publicapi",
args: mainline_framework_stubs_args,
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index bcef8ceaa941c60cce166785f96f4c3def141363..113f8fe9e248f793df3ec8bddd4338c53ec5030b 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -51,6 +51,7 @@ public final class BlobHandle implements Parcelable {
};
private static final int LIMIT_BLOB_TAG_LENGTH = 128; // characters
+ private static final int LIMIT_BLOB_LABEL_LENGTH = 100; // characters
/**
* Cyrptographically secure hash algorithm used to generate hash of the blob this handle is
@@ -128,6 +129,9 @@ public final class BlobHandle implements Parcelable {
*
* @param digest the SHA-256 hash of the blob this is representing.
* @param label a label indicating what the blob is, that can be surfaced to the user.
+ * The length of the label cannot be more than 100 characters. It is recommended
+ * to keep this brief. This may be truncated and ellipsized if it is too long
+ * to be displayed to the user.
* @param expiryTimeMillis the time in secs after which the blob should be invalidated and not
* allowed to be accessed by any other app,
* in {@link System#currentTimeMillis()} timebase or {@code 0} to
@@ -205,9 +209,9 @@ public final class BlobHandle implements Parcelable {
final BlobHandle other = (BlobHandle) obj;
return this.algorithm.equals(other.algorithm)
&& Arrays.equals(this.digest, other.digest)
- && this.label.equals(other.label)
+ && this.label.toString().equals(other.label.toString())
&& this.expiryTimeMillis == other.expiryTimeMillis
- && this.tag.equals(tag);
+ && this.tag.equals(other.tag);
}
@Override
@@ -219,7 +223,7 @@ public final class BlobHandle implements Parcelable {
public void dump(IndentingPrintWriter fout, boolean dumpFull) {
if (dumpFull) {
fout.println("algo: " + algorithm);
- fout.println("digest: " + (dumpFull ? encodeDigest() : safeDigest()));
+ fout.println("digest: " + (dumpFull ? encodeDigest(digest) : safeDigest(digest)));
fout.println("label: " + label);
fout.println("expiryMs: " + expiryTimeMillis);
fout.println("tag: " + tag);
@@ -233,6 +237,7 @@ public final class BlobHandle implements Parcelable {
Preconditions.checkArgumentIsSupported(SUPPORTED_ALGOS, algorithm);
Preconditions.checkByteArrayNotEmpty(digest, "digest");
Preconditions.checkStringNotEmpty(label, "label must not be null");
+ Preconditions.checkArgument(label.length() <= LIMIT_BLOB_LABEL_LENGTH, "label too long");
Preconditions.checkArgumentNonnegative(expiryTimeMillis,
"expiryTimeMillis must not be negative");
Preconditions.checkStringNotEmpty(tag, "tag must not be null");
@@ -243,19 +248,20 @@ public final class BlobHandle implements Parcelable {
public String toString() {
return "BlobHandle {"
+ "algo:" + algorithm + ","
- + "digest:" + safeDigest() + ","
+ + "digest:" + safeDigest(digest) + ","
+ "label:" + label + ","
+ "expiryMs:" + expiryTimeMillis + ","
+ "tag:" + tag
+ "}";
}
- private String safeDigest() {
- final String digestStr = encodeDigest();
+ /** @hide */
+ public static String safeDigest(@NonNull byte[] digest) {
+ final String digestStr = encodeDigest(digest);
return digestStr.substring(0, 2) + ".." + digestStr.substring(digestStr.length() - 2);
}
- private String encodeDigest() {
+ private static String encodeDigest(@NonNull byte[] digest) {
return Base64.encodeToString(digest, Base64.NO_WRAP);
}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
index 80062d5d245f825bcf8dc5fe8e108d31d3569dbb..ba92d95b483edd15767427eed2d3444fbc381f2c 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
@@ -16,9 +16,13 @@
package android.app.blob;
+import static android.text.format.Formatter.FLAG_IEC_UNITS;
+
import android.annotation.NonNull;
+import android.app.AppGlobals;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.format.Formatter;
import java.util.Collections;
import java.util.List;
@@ -32,13 +36,15 @@ public final class BlobInfo implements Parcelable {
private final long mId;
private final long mExpiryTimeMs;
private final CharSequence mLabel;
+ private final long mSizeBytes;
private final List mLeaseInfos;
- public BlobInfo(long id, long expiryTimeMs, CharSequence label,
+ public BlobInfo(long id, long expiryTimeMs, CharSequence label, long sizeBytes,
List leaseInfos) {
mId = id;
mExpiryTimeMs = expiryTimeMs;
mLabel = label;
+ mSizeBytes = sizeBytes;
mLeaseInfos = leaseInfos;
}
@@ -46,6 +52,7 @@ public final class BlobInfo implements Parcelable {
mId = in.readLong();
mExpiryTimeMs = in.readLong();
mLabel = in.readCharSequence();
+ mSizeBytes = in.readLong();
mLeaseInfos = in.readArrayList(null /* classloader */);
}
@@ -61,6 +68,10 @@ public final class BlobInfo implements Parcelable {
return mLabel;
}
+ public long getSizeBytes() {
+ return mSizeBytes;
+ }
+
public List getLeases() {
return Collections.unmodifiableList(mLeaseInfos);
}
@@ -70,6 +81,7 @@ public final class BlobInfo implements Parcelable {
dest.writeLong(mId);
dest.writeLong(mExpiryTimeMs);
dest.writeCharSequence(mLabel);
+ dest.writeLong(mSizeBytes);
dest.writeList(mLeaseInfos);
}
@@ -83,10 +95,16 @@ public final class BlobInfo implements Parcelable {
+ "id: " + mId + ","
+ "expiryMs: " + mExpiryTimeMs + ","
+ "label: " + mLabel + ","
+ + "size: " + formatBlobSize(mSizeBytes) + ","
+ "leases: " + LeaseInfo.toShortString(mLeaseInfos) + ","
+ "}";
}
+ private static String formatBlobSize(long sizeBytes) {
+ return Formatter.formatFileSize(AppGlobals.getInitialApplication(),
+ sizeBytes, FLAG_IEC_UNITS);
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 483d2cc2ec57aca719af81acebfc93dd82b08e3a..39f7526560a9ae9e8fe8087f437f3c44243f5632 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -184,9 +184,8 @@ public class BlobStoreManager {
* @throws SecurityException when the caller is not allowed to create a session, such
* as when called from an Instant app.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
- * @throws IllegalStateException when a new session could not be created, such as when the
- * caller is trying to create too many sessions or when the
- * device is running low on space.
+ * @throws LimitExceededException when a new session could not be created, such as when the
+ * caller is trying to create too many sessions.
*/
public @IntRange(from = 1) long createSession(@NonNull BlobHandle blobHandle)
throws IOException {
@@ -194,6 +193,7 @@ public class BlobStoreManager {
return mService.createSession(blobHandle, mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
+ e.maybeRethrow(LimitExceededException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -302,8 +302,9 @@ public class BlobStoreManager {
* if the {@code leaseExpiryTimeMillis} is greater than the
* {@link BlobHandle#getExpiryTimeMillis()}.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -346,7 +347,9 @@ public class BlobStoreManager {
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
* acquire a lease for.
* @param description a short description string that can be surfaced
- * to the user explaining what the blob is used for.
+ * to the user explaining what the blob is used for. It is recommended to
+ * keep this description brief. This may be truncated and ellipsized
+ * if it is too long to be displayed to the user.
* @param leaseExpiryTimeMillis the time in milliseconds after which the lease can be
* automatically released, in {@link System#currentTimeMillis()}
* timebase. If its value is {@code 0}, then the behavior of this
@@ -362,8 +365,9 @@ public class BlobStoreManager {
* if the {@code leaseExpiryTimeMillis} is greater than the
* {@link BlobHandle#getExpiryTimeMillis()}.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -415,8 +419,9 @@ public class BlobStoreManager {
* exist or the caller does not have access to it.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -455,15 +460,18 @@ public class BlobStoreManager {
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
* acquire a lease for.
* @param description a short description string that can be surfaced
- * to the user explaining what the blob is used for.
+ * to the user explaining what the blob is used for. It is recommended to
+ * keep this description brief. This may be truncated and
+ * ellipsized if it is too long to be displayed to the user.
*
* @throws IOException when there is an I/O error while acquiring a lease to the blob.
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
* exist or the caller does not have access to it.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -757,6 +765,8 @@ public class BlobStoreManager {
* @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.
+ * @throws LimitExceededException when the caller tries to explicitly allow too
+ * many packages using this API.
*/
public void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate)
throws IOException {
@@ -764,6 +774,7 @@ public class BlobStoreManager {
mSession.allowPackageAccess(packageName, certificate);
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
+ e.maybeRethrow(LimitExceededException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
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 ec7ba287acec78a73ca7a21d2756caf224911c46..ba0fab6b4bc525839221d8a67d23a8fb43742d16 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
@@ -44,7 +44,7 @@ import java.util.Objects;
/**
* Class for representing how a blob can be shared.
*
- * Note that this class is not thread-safe, callers need to take of synchronizing access.
+ * Note that this class is not thread-safe, callers need to take care of synchronizing access.
*/
class BlobAccessMode {
@Retention(RetentionPolicy.SOURCE)
@@ -127,6 +127,14 @@ class BlobAccessMode {
return false;
}
+ int getAccessType() {
+ return mAccessType;
+ }
+
+ int getNumWhitelistedPackages() {
+ return mWhitelistedPackages.size();
+ }
+
void dump(IndentingPrintWriter fout) {
fout.println("accessType: " + DebugUtils.flagsToString(
BlobAccessMode.class, "ACCESS_TYPE_", mAccessType));
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 cea7fcca6e2084abd330c6ba0c19e8d1d3d699d0..0b760a621d22808e15cb3a21d4e697cfb3419321 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -29,6 +29,8 @@ import static android.app.blob.XmlTags.TAG_COMMITTER;
import static android.app.blob.XmlTags.TAG_LEASEE;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.O_RDONLY;
+import static android.text.format.Formatter.FLAG_IEC_UNITS;
+import static android.text.format.Formatter.formatFileSize;
import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_COMMIT_TIME;
@@ -54,6 +56,8 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.StatsEvent;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -61,6 +65,8 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -148,10 +154,10 @@ class BlobMetadata {
}
}
- void removeInvalidCommitters(SparseArray packages) {
+ void removeCommittersFromUnknownPkgs(SparseArray knownPackages) {
synchronized (mMetadataLock) {
mCommitters.removeIf(committer ->
- !committer.packageName.equals(packages.get(committer.uid)));
+ !committer.packageName.equals(knownPackages.get(committer.uid)));
}
}
@@ -194,16 +200,27 @@ class BlobMetadata {
}
}
- void removeInvalidLeasees(SparseArray packages) {
+ void removeLeaseesFromUnknownPkgs(SparseArray knownPackages) {
synchronized (mMetadataLock) {
mLeasees.removeIf(leasee ->
- !leasee.packageName.equals(packages.get(leasee.uid)));
+ !leasee.packageName.equals(knownPackages.get(leasee.uid)));
+ }
+ }
+
+ void removeExpiredLeases() {
+ synchronized (mMetadataLock) {
+ mLeasees.removeIf(leasee -> !leasee.isStillValid());
}
}
- boolean hasLeases() {
+ boolean hasValidLeases() {
synchronized (mMetadataLock) {
- return !mLeasees.isEmpty();
+ for (int i = 0, size = mLeasees.size(); i < size; ++i) {
+ if (mLeasees.valueAt(i).isStillValid()) {
+ return true;
+ }
+ }
+ return false;
}
}
@@ -220,8 +237,7 @@ class BlobMetadata {
// Check if packageName already holds a lease on the blob.
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
final Leasee leasee = mLeasees.valueAt(i);
- if (leasee.equals(callingPackage, callingUid)
- && leasee.isStillValid()) {
+ if (leasee.isStillValid() && leasee.equals(callingPackage, callingUid)) {
return true;
}
}
@@ -253,25 +269,32 @@ class BlobMetadata {
boolean isALeasee(@Nullable String packageName, int uid) {
synchronized (mMetadataLock) {
- return isAnAccessor(mLeasees, packageName, uid);
+ final Leasee leasee = getAccessor(mLeasees, packageName, uid);
+ return leasee != null && leasee.isStillValid();
}
}
private static boolean isAnAccessor(@NonNull ArraySet accessors,
@Nullable String packageName, int uid) {
// Check if the package is an accessor of the data blob.
+ return getAccessor(accessors, packageName, uid) != null;
+ }
+
+ private static T getAccessor(@NonNull ArraySet accessors,
+ @Nullable String packageName, int uid) {
+ // Check if the package is an accessor of the data blob.
for (int i = 0, size = accessors.size(); i < size; ++i) {
final Accessor accessor = accessors.valueAt(i);
if (packageName != null && uid != INVALID_UID
&& accessor.equals(packageName, uid)) {
- return true;
+ return (T) accessor;
} else if (packageName != null && accessor.packageName.equals(packageName)) {
- return true;
+ return (T) accessor;
} else if (uid != INVALID_UID && accessor.uid == uid) {
- return true;
+ return (T) accessor;
}
}
- return false;
+ return null;
}
boolean isALeasee(@NonNull String packageName) {
@@ -292,11 +315,11 @@ class BlobMetadata {
private boolean hasOtherLeasees(@Nullable String packageName, int uid) {
synchronized (mMetadataLock) {
- if (mCommitters.size() > 1 || mLeasees.size() > 1) {
- return true;
- }
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
final Leasee leasee = mLeasees.valueAt(i);
+ if (!leasee.isStillValid()) {
+ continue;
+ }
// TODO: Also exclude packages which are signed with same cert?
if (packageName != null && uid != INVALID_UID
&& !leasee.equals(packageName, uid)) {
@@ -316,6 +339,9 @@ class BlobMetadata {
synchronized (mMetadataLock) {
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
final Leasee leasee = mLeasees.valueAt(i);
+ if (!leasee.isStillValid()) {
+ continue;
+ }
if (leasee.uid == uid && leasee.packageName.equals(packageName)) {
final int descriptionResId = leasee.descriptionResEntryName == null
? Resources.ID_NULL
@@ -331,7 +357,9 @@ class BlobMetadata {
}
void forEachLeasee(Consumer consumer) {
- mLeasees.forEach(consumer);
+ synchronized (mMetadataLock) {
+ mLeasees.forEach(consumer);
+ }
}
File getBlobFile() {
@@ -349,14 +377,20 @@ class BlobMetadata {
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
- synchronized (mMetadataLock) {
- return createRevocableFdLocked(fd, callingPackage);
+ try {
+ if (BlobStoreConfig.shouldUseRevocableFdForReads()) {
+ return createRevocableFd(fd, callingPackage);
+ } else {
+ return new ParcelFileDescriptor(fd);
+ }
+ } catch (IOException e) {
+ IoUtils.closeQuietly(fd);
+ throw e;
}
}
- @GuardedBy("mMetadataLock")
@NonNull
- private ParcelFileDescriptor createRevocableFdLocked(FileDescriptor fd,
+ private ParcelFileDescriptor createRevocableFd(FileDescriptor fd,
String callingPackage) throws IOException {
final RevocableFileDescriptor revocableFd =
new RevocableFileDescriptor(mContext, fd);
@@ -384,6 +418,26 @@ class BlobMetadata {
return revocableFd.getRevocableFileDescriptor();
}
+ void destroy() {
+ revokeAllFds();
+ getBlobFile().delete();
+ }
+
+ private void revokeAllFds() {
+ synchronized (mRevocableFds) {
+ for (int i = 0, pkgCount = mRevocableFds.size(); i < pkgCount; ++i) {
+ final ArraySet packageFds =
+ mRevocableFds.valueAt(i);
+ if (packageFds == null) {
+ continue;
+ }
+ for (int j = 0, fdCount = packageFds.size(); j < fdCount; ++j) {
+ packageFds.valueAt(j).revoke();
+ }
+ }
+ }
+ }
+
boolean shouldBeDeleted(boolean respectLeaseWaitTime) {
// Expired data blobs
if (getBlobHandle().isExpired()) {
@@ -392,7 +446,7 @@ class BlobMetadata {
// Blobs with no active leases
if ((!respectLeaseWaitTime || hasLeaseWaitTimeElapsedForAll())
- && !hasLeases()) {
+ && !hasValidLeases()) {
return true;
}
@@ -410,55 +464,101 @@ class BlobMetadata {
return true;
}
- void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
- fout.println("blobHandle:");
- fout.increaseIndent();
- mBlobHandle.dump(fout, dumpArgs.shouldDumpFull());
- fout.decreaseIndent();
-
- fout.println("Committers:");
- fout.increaseIndent();
- if (mCommitters.isEmpty()) {
- fout.println("");
- } else {
- for (int i = 0, count = mCommitters.size(); i < count; ++i) {
+ StatsEvent dumpAsStatsEvent(int atomTag) {
+ synchronized (mMetadataLock) {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ // Write Committer data to proto format
+ for (int i = 0, size = mCommitters.size(); i < size; ++i) {
final Committer committer = mCommitters.valueAt(i);
- fout.println("committer " + committer.toString());
- fout.increaseIndent();
- committer.dump(fout);
- fout.decreaseIndent();
+ final long token = proto.start(
+ BlobStatsEventProto.BlobCommitterListProto.COMMITTER);
+ proto.write(BlobStatsEventProto.BlobCommitterProto.UID, committer.uid);
+ proto.write(BlobStatsEventProto.BlobCommitterProto.COMMIT_TIMESTAMP_MILLIS,
+ committer.commitTimeMs);
+ proto.write(BlobStatsEventProto.BlobCommitterProto.ACCESS_MODE,
+ committer.blobAccessMode.getAccessType());
+ proto.write(BlobStatsEventProto.BlobCommitterProto.NUM_WHITELISTED_PACKAGE,
+ committer.blobAccessMode.getNumWhitelistedPackages());
+ proto.end(token);
}
- }
- fout.decreaseIndent();
+ final byte[] committersBytes = proto.getBytes();
- fout.println("Leasees:");
- fout.increaseIndent();
- if (mLeasees.isEmpty()) {
- fout.println("");
- } else {
- for (int i = 0, count = mLeasees.size(); i < count; ++i) {
+ proto = new ProtoOutputStream();
+ // Write Leasee data to proto format
+ for (int i = 0, size = mLeasees.size(); i < size; ++i) {
final Leasee leasee = mLeasees.valueAt(i);
- fout.println("leasee " + leasee.toString());
- fout.increaseIndent();
- leasee.dump(mContext, fout);
- fout.decreaseIndent();
+ final long token = proto.start(BlobStatsEventProto.BlobLeaseeListProto.LEASEE);
+ proto.write(BlobStatsEventProto.BlobLeaseeProto.UID, leasee.uid);
+ proto.write(BlobStatsEventProto.BlobLeaseeProto.LEASE_EXPIRY_TIMESTAMP_MILLIS,
+ leasee.expiryTimeMillis);
+ proto.end(token);
}
+ final byte[] leaseesBytes = proto.getBytes();
+
+ // Construct the StatsEvent to represent this Blob
+ return StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeLong(mBlobId)
+ .writeLong(getSize())
+ .writeLong(mBlobHandle.getExpiryTimeMillis())
+ .writeByteArray(committersBytes)
+ .writeByteArray(leaseesBytes)
+ .build();
}
- fout.decreaseIndent();
+ }
- fout.println("Open fds:");
- fout.increaseIndent();
- if (mRevocableFds.isEmpty()) {
- fout.println("");
- } else {
- for (int i = 0, count = mRevocableFds.size(); i < count; ++i) {
- final String packageName = mRevocableFds.keyAt(i);
- final ArraySet packageFds =
- mRevocableFds.valueAt(i);
- fout.println(packageName + "#" + packageFds.size());
+ void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
+ synchronized (mMetadataLock) {
+ fout.println("blobHandle:");
+ fout.increaseIndent();
+ mBlobHandle.dump(fout, dumpArgs.shouldDumpFull());
+ fout.decreaseIndent();
+ fout.println("size: " + formatFileSize(mContext, getSize(), FLAG_IEC_UNITS));
+
+ fout.println("Committers:");
+ fout.increaseIndent();
+ if (mCommitters.isEmpty()) {
+ fout.println("");
+ } else {
+ for (int i = 0, count = mCommitters.size(); i < count; ++i) {
+ final Committer committer = mCommitters.valueAt(i);
+ fout.println("committer " + committer.toString());
+ fout.increaseIndent();
+ committer.dump(fout);
+ fout.decreaseIndent();
+ }
+ }
+ fout.decreaseIndent();
+
+ fout.println("Leasees:");
+ fout.increaseIndent();
+ if (mLeasees.isEmpty()) {
+ fout.println("");
+ } else {
+ for (int i = 0, count = mLeasees.size(); i < count; ++i) {
+ final Leasee leasee = mLeasees.valueAt(i);
+ fout.println("leasee " + leasee.toString());
+ fout.increaseIndent();
+ leasee.dump(mContext, fout);
+ fout.decreaseIndent();
+ }
}
+ fout.decreaseIndent();
+
+ fout.println("Open fds:");
+ fout.increaseIndent();
+ if (mRevocableFds.isEmpty()) {
+ fout.println("");
+ } else {
+ for (int i = 0, count = mRevocableFds.size(); i < count; ++i) {
+ final String packageName = mRevocableFds.keyAt(i);
+ final ArraySet packageFds =
+ mRevocableFds.valueAt(i);
+ fout.println(packageName + "#" + packageFds.size());
+ }
+ }
+ fout.decreaseIndent();
}
- fout.decreaseIndent();
}
void writeToXml(XmlSerializer out) throws IOException {
@@ -635,7 +735,7 @@ class BlobMetadata {
}
boolean isStillValid() {
- return expiryTimeMillis == 0 || expiryTimeMillis <= System.currentTimeMillis();
+ return expiryTimeMillis == 0 || expiryTimeMillis >= System.currentTimeMillis();
}
void dump(@NonNull Context context, @NonNull IndentingPrintWriter fout) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index 656726622cfda3ad5069c00d5f30e9236781fb1c..bb9f13f1712c5c7d2430dbacab9dc221ffac5df5 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.os.Environment;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
+import android.text.TextUtils;
import android.util.DataUnit;
import android.util.Log;
import android.util.Slog;
@@ -49,6 +50,9 @@ class BlobStoreConfig {
public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_SESSION_CREATION_TIME;
+ public static final long INVALID_BLOB_ID = 0;
+ public static final long INVALID_BLOB_SIZE = 0;
+
private static final String ROOT_DIR_NAME = "blobstore";
private static final String BLOBS_DIR_NAME = "blobs";
private static final String SESSIONS_INDEX_FILE_NAME = "sessions_index.xml";
@@ -119,6 +123,62 @@ class BlobStoreConfig {
public static long COMMIT_COOL_OFF_DURATION_MS =
DEFAULT_COMMIT_COOL_OFF_DURATION_MS;
+ /**
+ * Denotes whether to use RevocableFileDescriptor when apps try to read session/blob data.
+ */
+ public static final String KEY_USE_REVOCABLE_FD_FOR_READS =
+ "use_revocable_fd_for_reads";
+ public static final boolean DEFAULT_USE_REVOCABLE_FD_FOR_READS = true;
+ public static boolean USE_REVOCABLE_FD_FOR_READS =
+ DEFAULT_USE_REVOCABLE_FD_FOR_READS;
+
+ /**
+ * Denotes how long before a blob is deleted, once the last lease on it is released.
+ */
+ public static final String KEY_DELETE_ON_LAST_LEASE_DELAY_MS =
+ "delete_on_last_lease_delay_ms";
+ public static final long DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS =
+ TimeUnit.HOURS.toMillis(6);
+ public static long DELETE_ON_LAST_LEASE_DELAY_MS =
+ DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS;
+
+ /**
+ * Denotes the maximum number of active sessions per app at any time.
+ */
+ public static final String KEY_MAX_ACTIVE_SESSIONS = "max_active_sessions";
+ public static int DEFAULT_MAX_ACTIVE_SESSIONS = 250;
+ public static int MAX_ACTIVE_SESSIONS = DEFAULT_MAX_ACTIVE_SESSIONS;
+
+ /**
+ * Denotes the maximum number of committed blobs per app at any time.
+ */
+ public static final String KEY_MAX_COMMITTED_BLOBS = "max_committed_blobs";
+ public static int DEFAULT_MAX_COMMITTED_BLOBS = 1000;
+ public static int MAX_COMMITTED_BLOBS = DEFAULT_MAX_COMMITTED_BLOBS;
+
+ /**
+ * Denotes the maximum number of leased blobs per app at any time.
+ */
+ public static final String KEY_MAX_LEASED_BLOBS = "max_leased_blobs";
+ public static int DEFAULT_MAX_LEASED_BLOBS = 500;
+ public static int MAX_LEASED_BLOBS = DEFAULT_MAX_LEASED_BLOBS;
+
+ /**
+ * Denotes the maximum number of packages explicitly permitted to access a blob
+ * (permitted as part of creating a {@link BlobAccessMode}).
+ */
+ public static final String KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = "max_permitted_pks";
+ public static int DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = 300;
+ public static int MAX_BLOB_ACCESS_PERMITTED_PACKAGES =
+ DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
+
+ /**
+ * Denotes the maximum number of characters that a lease description can have.
+ */
+ public static final String KEY_LEASE_DESC_CHAR_LIMIT = "lease_desc_char_limit";
+ public static int DEFAULT_LEASE_DESC_CHAR_LIMIT = 300;
+ public static int LEASE_DESC_CHAR_LIMIT = DEFAULT_LEASE_DESC_CHAR_LIMIT;
+
static void refresh(Properties properties) {
if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) {
return;
@@ -148,6 +208,31 @@ class BlobStoreConfig {
COMMIT_COOL_OFF_DURATION_MS = properties.getLong(key,
DEFAULT_COMMIT_COOL_OFF_DURATION_MS);
break;
+ case KEY_USE_REVOCABLE_FD_FOR_READS:
+ USE_REVOCABLE_FD_FOR_READS = properties.getBoolean(key,
+ DEFAULT_USE_REVOCABLE_FD_FOR_READS);
+ break;
+ case KEY_DELETE_ON_LAST_LEASE_DELAY_MS:
+ DELETE_ON_LAST_LEASE_DELAY_MS = properties.getLong(key,
+ DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS);
+ break;
+ case KEY_MAX_ACTIVE_SESSIONS:
+ MAX_ACTIVE_SESSIONS = properties.getInt(key, DEFAULT_MAX_ACTIVE_SESSIONS);
+ break;
+ case KEY_MAX_COMMITTED_BLOBS:
+ MAX_COMMITTED_BLOBS = properties.getInt(key, DEFAULT_MAX_COMMITTED_BLOBS);
+ break;
+ case KEY_MAX_LEASED_BLOBS:
+ MAX_LEASED_BLOBS = properties.getInt(key, DEFAULT_MAX_LEASED_BLOBS);
+ break;
+ case KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES:
+ MAX_BLOB_ACCESS_PERMITTED_PACKAGES = properties.getInt(key,
+ DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES);
+ break;
+ case KEY_LEASE_DESC_CHAR_LIMIT:
+ LEASE_DESC_CHAR_LIMIT = properties.getInt(key,
+ DEFAULT_LEASE_DESC_CHAR_LIMIT);
+ break;
default:
Slog.wtf(TAG, "Unknown key in device config properties: " + key);
}
@@ -175,6 +260,22 @@ class BlobStoreConfig {
fout.println(String.format(dumpFormat, KEY_COMMIT_COOL_OFF_DURATION_MS,
TimeUtils.formatDuration(COMMIT_COOL_OFF_DURATION_MS),
TimeUtils.formatDuration(DEFAULT_COMMIT_COOL_OFF_DURATION_MS)));
+ fout.println(String.format(dumpFormat, KEY_USE_REVOCABLE_FD_FOR_READS,
+ USE_REVOCABLE_FD_FOR_READS, DEFAULT_USE_REVOCABLE_FD_FOR_READS));
+ fout.println(String.format(dumpFormat, KEY_DELETE_ON_LAST_LEASE_DELAY_MS,
+ TimeUtils.formatDuration(DELETE_ON_LAST_LEASE_DELAY_MS),
+ TimeUtils.formatDuration(DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS)));
+ fout.println(String.format(dumpFormat, KEY_MAX_ACTIVE_SESSIONS,
+ MAX_ACTIVE_SESSIONS, DEFAULT_MAX_ACTIVE_SESSIONS));
+ fout.println(String.format(dumpFormat, KEY_MAX_COMMITTED_BLOBS,
+ MAX_COMMITTED_BLOBS, DEFAULT_MAX_COMMITTED_BLOBS));
+ fout.println(String.format(dumpFormat, KEY_MAX_LEASED_BLOBS,
+ MAX_LEASED_BLOBS, DEFAULT_MAX_LEASED_BLOBS));
+ fout.println(String.format(dumpFormat, KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
+ MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
+ DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES));
+ fout.println(String.format(dumpFormat, KEY_LEASE_DESC_CHAR_LIMIT,
+ LEASE_DESC_CHAR_LIMIT, DEFAULT_LEASE_DESC_CHAR_LIMIT));
}
}
@@ -239,6 +340,60 @@ class BlobStoreConfig {
< System.currentTimeMillis();
}
+ /**
+ * Return whether to use RevocableFileDescriptor when apps try to read session/blob data.
+ */
+ public static boolean shouldUseRevocableFdForReads() {
+ return DeviceConfigProperties.USE_REVOCABLE_FD_FOR_READS;
+ }
+
+ /**
+ * Returns the duration to wait before a blob is deleted, once the last lease on it is released.
+ */
+ public static long getDeletionOnLastLeaseDelayMs() {
+ return DeviceConfigProperties.DELETE_ON_LAST_LEASE_DELAY_MS;
+ }
+
+ /**
+ * Returns the maximum number of active sessions per app.
+ */
+ public static int getMaxActiveSessions() {
+ return DeviceConfigProperties.MAX_ACTIVE_SESSIONS;
+ }
+
+ /**
+ * Returns the maximum number of committed blobs per app.
+ */
+ public static int getMaxCommittedBlobs() {
+ return DeviceConfigProperties.MAX_COMMITTED_BLOBS;
+ }
+
+ /**
+ * Returns the maximum number of leased blobs per app.
+ */
+ public static int getMaxLeasedBlobs() {
+ return DeviceConfigProperties.MAX_LEASED_BLOBS;
+ }
+
+ /**
+ * Returns the maximum number of packages explicitly permitted to access a blob.
+ */
+ public static int getMaxPermittedPackages() {
+ return DeviceConfigProperties.MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
+ }
+
+ /**
+ * Returns the lease description truncated to
+ * {@link DeviceConfigProperties#LEASE_DESC_CHAR_LIMIT} characters.
+ */
+ public static CharSequence getTruncatedLeaseDescription(CharSequence description) {
+ if (TextUtils.isEmpty(description)) {
+ return description;
+ }
+ return TextUtils.trimToLengthWithEllipsis(description,
+ DeviceConfigProperties.LEASE_DESC_CHAR_LIMIT);
+ }
+
@Nullable
public static File prepareBlobFile(long sessionId) {
final File blobsDir = prepareBlobsDir();
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 8fff0d9c950b4c73a2bb4835d45eaf7ae7bb8290..d37dfdeaa58378ec0d116bbd2c2eedb38df34d9f 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -28,10 +28,16 @@ import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.os.UserHandle.USER_CURRENT;
import static android.os.UserHandle.USER_NULL;
+import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_ID;
+import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_SIZE;
import static com.android.server.blob.BlobStoreConfig.LOGV;
import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
import static com.android.server.blob.BlobStoreConfig.getAdjustedCommitTimeMs;
+import static com.android.server.blob.BlobStoreConfig.getDeletionOnLastLeaseDelayMs;
+import static com.android.server.blob.BlobStoreConfig.getMaxActiveSessions;
+import static com.android.server.blob.BlobStoreConfig.getMaxCommittedBlobs;
+import static com.android.server.blob.BlobStoreConfig.getMaxLeasedBlobs;
import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED;
import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
@@ -48,6 +54,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.StatsManager;
import android.app.blob.BlobHandle;
import android.app.blob.BlobInfo;
import android.app.blob.IBlobStoreManager;
@@ -80,6 +87,7 @@ import android.util.ExceptionUtils;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.StatsEvent;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -88,6 +96,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
@@ -118,6 +127,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -159,6 +169,8 @@ public class BlobStoreManagerService extends SystemService {
new SessionStateChangeListener();
private PackageManagerInternal mPackageManagerInternal;
+ private StatsManager mStatsManager;
+ private StatsPullAtomCallbackImpl mStatsCallbackImpl = new StatsPullAtomCallbackImpl();
private final Runnable mSaveBlobsInfoRunnable = this::writeBlobsInfo;
private final Runnable mSaveSessionsRunnable = this::writeBlobSessions;
@@ -192,6 +204,7 @@ public class BlobStoreManagerService extends SystemService {
LocalServices.addService(BlobStoreManagerInternal.class, new LocalService());
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mStatsManager = getContext().getSystemService(StatsManager.class);
registerReceivers();
LocalServices.getService(StorageStatsManagerInternal.class)
.registerStorageStatsAugmenter(new BlobStorageStatsAugmenter(), TAG);
@@ -207,6 +220,7 @@ public class BlobStoreManagerService extends SystemService {
readBlobSessionsLocked(allPackages);
readBlobsInfoLocked(allPackages);
}
+ registerBlobStorePuller();
} else if (phase == PHASE_BOOT_COMPLETED) {
BlobStoreIdleJobService.schedule(mContext);
}
@@ -218,8 +232,9 @@ public class BlobStoreManagerService extends SystemService {
int n = 0;
long sessionId;
do {
- sessionId = Math.abs(mRandom.nextLong());
- if (mKnownBlobIds.indexOf(sessionId) < 0 && sessionId != 0) {
+ final long randomLong = mRandom.nextLong();
+ sessionId = (randomLong == Long.MIN_VALUE) ? INVALID_BLOB_ID : Math.abs(randomLong);
+ if (mKnownBlobIds.indexOf(sessionId) < 0 && sessionId != INVALID_BLOB_ID) {
return sessionId;
}
} while (n++ < 32);
@@ -321,9 +336,26 @@ public class BlobStoreManagerService extends SystemService {
mKnownBlobIds.add(id);
}
+ @GuardedBy("mBlobsLock")
+ private int getSessionsCountLocked(int uid, String packageName) {
+ // TODO: Maintain a counter instead of traversing all the sessions
+ final AtomicInteger sessionsCount = new AtomicInteger(0);
+ forEachSessionInUser(session -> {
+ if (session.getOwnerUid() == uid && session.getOwnerPackageName().equals(packageName)) {
+ sessionsCount.getAndIncrement();
+ }
+ }, UserHandle.getUserId(uid));
+ return sessionsCount.get();
+ }
+
private long createSessionInternal(BlobHandle blobHandle,
int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
+ final int sessionsCount = getSessionsCountLocked(callingUid, callingPackage);
+ if (sessionsCount >= getMaxActiveSessions()) {
+ throw new LimitExceededException("Too many active sessions for the caller: "
+ + sessionsCount);
+ }
// TODO: throw if there is already an active session associated with blobHandle.
final long sessionId = generateNextSessionIdLocked();
final BlobStoreSession session = new BlobStoreSession(mContext,
@@ -376,34 +408,102 @@ public class BlobStoreManagerService extends SystemService {
.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
callingPackage, callingUid)) {
+ if (blobMetadata == null) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
+ INVALID_BLOB_ID, INVALID_BLOB_SIZE,
+ FrameworkStatsLog.BLOB_OPENED__RESULT__BLOB_DNE);
+ } else {
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
+ blobMetadata.getBlobId(), blobMetadata.getSize(),
+ FrameworkStatsLog.BLOB_LEASED__RESULT__ACCESS_NOT_ALLOWED);
+ }
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
+ blobMetadata.getBlobId(), blobMetadata.getSize(),
+ FrameworkStatsLog.BLOB_OPENED__RESULT__SUCCESS);
+
return blobMetadata.openForRead(callingPackage);
}
}
+ @GuardedBy("mBlobsLock")
+ private int getCommittedBlobsCountLocked(int uid, String packageName) {
+ // TODO: Maintain a counter instead of traversing all the blobs
+ final AtomicInteger blobsCount = new AtomicInteger(0);
+ forEachBlobInUser((blobMetadata) -> {
+ if (blobMetadata.isACommitter(packageName, uid)) {
+ blobsCount.getAndIncrement();
+ }
+ }, UserHandle.getUserId(uid));
+ return blobsCount.get();
+ }
+
+ @GuardedBy("mBlobsLock")
+ private int getLeasedBlobsCountLocked(int uid, String packageName) {
+ // TODO: Maintain a counter instead of traversing all the blobs
+ final AtomicInteger blobsCount = new AtomicInteger(0);
+ forEachBlobInUser((blobMetadata) -> {
+ if (blobMetadata.isALeasee(packageName, uid)) {
+ blobsCount.getAndIncrement();
+ }
+ }, UserHandle.getUserId(uid));
+ return blobsCount.get();
+ }
+
private void acquireLeaseInternal(BlobHandle blobHandle, int descriptionResId,
CharSequence description, long leaseExpiryTimeMillis,
int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
+ final int leasesCount = getLeasedBlobsCountLocked(callingUid, callingPackage);
+ if (leasesCount >= getMaxLeasedBlobs()) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
+ INVALID_BLOB_ID, INVALID_BLOB_SIZE,
+ FrameworkStatsLog.BLOB_LEASED__RESULT__COUNT_LIMIT_EXCEEDED);
+ throw new LimitExceededException("Too many leased blobs for the caller: "
+ + leasesCount);
+ }
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
callingPackage, callingUid)) {
+ if (blobMetadata == null) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
+ INVALID_BLOB_ID, INVALID_BLOB_SIZE,
+ FrameworkStatsLog.BLOB_LEASED__RESULT__BLOB_DNE);
+ } else {
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
+ blobMetadata.getBlobId(), blobMetadata.getSize(),
+ FrameworkStatsLog.BLOB_LEASED__RESULT__ACCESS_NOT_ALLOWED);
+ }
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0
&& leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
+ blobMetadata.getBlobId(), blobMetadata.getSize(),
+ FrameworkStatsLog.BLOB_LEASED__RESULT__LEASE_EXPIRY_INVALID);
throw new IllegalArgumentException(
"Lease expiry cannot be later than blobs expiry time");
}
if (blobMetadata.getSize()
> getRemainingLeaseQuotaBytesInternal(callingUid, callingPackage)) {
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
+ blobMetadata.getBlobId(), blobMetadata.getSize(),
+ FrameworkStatsLog.BLOB_LEASED__RESULT__DATA_SIZE_LIMIT_EXCEEDED);
throw new LimitExceededException("Total amount of data with an active lease"
+ " is exceeding the max limit");
}
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
+ blobMetadata.getBlobId(), blobMetadata.getSize(),
+ FrameworkStatsLog.BLOB_LEASED__RESULT__SUCCESS);
+
blobMetadata.addOrReplaceLeasee(callingPackage, callingUid,
descriptionResId, description, leaseExpiryTimeMillis);
if (LOGV) {
@@ -442,9 +542,21 @@ public class BlobStoreManagerService extends SystemService {
Slog.v(TAG, "Released lease on " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
- if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
- deleteBlobLocked(blobMetadata);
- userBlobs.remove(blobHandle);
+ if (!blobMetadata.hasValidLeases()) {
+ mHandler.postDelayed(() -> {
+ synchronized (mBlobsLock) {
+ // Check if blobMetadata object is still valid. If it is not, then
+ // it means that it was already deleted and nothing else to do here.
+ if (!Objects.equals(userBlobs.get(blobHandle), blobMetadata)) {
+ return;
+ }
+ if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
+ deleteBlobLocked(blobMetadata);
+ userBlobs.remove(blobHandle);
+ }
+ writeBlobsInfoAsync();
+ }
+ }, getDeletionOnLastLeaseDelayMs());
}
writeBlobsInfoAsync();
}
@@ -474,15 +586,21 @@ public class BlobStoreManagerService extends SystemService {
getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> {
final ArrayList leaseInfos = new ArrayList<>();
blobMetadata.forEachLeasee(leasee -> {
+ if (!leasee.isStillValid()) {
+ return;
+ }
final int descriptionResId = leasee.descriptionResEntryName == null
? Resources.ID_NULL
: getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
leasee.descriptionResEntryName, leasee.packageName);
- leaseInfos.add(new LeaseInfo(leasee.packageName, leasee.expiryTimeMillis,
+ final long expiryTimeMs = leasee.expiryTimeMillis == 0
+ ? blobHandle.getExpiryTimeMillis() : leasee.expiryTimeMillis;
+ leaseInfos.add(new LeaseInfo(leasee.packageName, expiryTimeMs,
descriptionResId, leasee.description));
});
blobInfos.add(new BlobInfo(blobMetadata.getBlobId(),
- blobHandle.getExpiryTimeMillis(), blobHandle.getLabel(), leaseInfos));
+ blobHandle.getExpiryTimeMillis(), blobHandle.getLabel(),
+ blobMetadata.getSize(), leaseInfos));
});
}
return blobInfos;
@@ -494,7 +612,11 @@ public class BlobStoreManagerService extends SystemService {
UserHandle.getUserId(callingUid));
userBlobs.entrySet().removeIf(entry -> {
final BlobMetadata blobMetadata = entry.getValue();
- return blobMetadata.getBlobId() == blobId;
+ if (blobMetadata.getBlobId() == blobId) {
+ deleteBlobLocked(blobMetadata);
+ return true;
+ }
+ return false;
});
writeBlobsInfoAsync();
}
@@ -545,11 +667,10 @@ public class BlobStoreManagerService extends SystemService {
switch (session.getState()) {
case STATE_ABANDONED:
case STATE_VERIFIED_INVALID:
- session.getSessionFile().delete();
synchronized (mBlobsLock) {
+ deleteSessionLocked(session);
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
.remove(session.getSessionId());
- mActiveBlobIds.remove(session.getSessionId());
if (LOGV) {
Slog.v(TAG, "Session is invalid; deleted " + session);
}
@@ -564,6 +685,20 @@ public class BlobStoreManagerService extends SystemService {
break;
case STATE_VERIFIED_VALID:
synchronized (mBlobsLock) {
+ final int committedBlobsCount = getCommittedBlobsCountLocked(
+ session.getOwnerUid(), session.getOwnerPackageName());
+ if (committedBlobsCount >= getMaxCommittedBlobs()) {
+ Slog.d(TAG, "Failed to commit: too many committed blobs. count: "
+ + committedBlobsCount + "; blob: " + session);
+ session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
+ deleteSessionLocked(session);
+ getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
+ .remove(session.getSessionId());
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
+ session.getOwnerUid(), session.getSessionId(), session.getSize(),
+ FrameworkStatsLog.BLOB_COMMITTED__RESULT__COUNT_LIMIT_EXCEEDED);
+ break;
+ }
final int userId = UserHandle.getUserId(session.getOwnerUid());
final ArrayMap userBlobs = getUserBlobsLocked(
userId);
@@ -584,6 +719,9 @@ public class BlobStoreManagerService extends SystemService {
blob.addOrReplaceCommitter(newCommitter);
try {
writeBlobsInfoLocked();
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
+ session.getOwnerUid(), blob.getBlobId(), blob.getSize(),
+ FrameworkStatsLog.BLOB_COMMITTED__RESULT__SUCCESS);
session.sendCommitCallbackResult(COMMIT_RESULT_SUCCESS);
} catch (Exception e) {
if (existingCommitter == null) {
@@ -591,7 +729,21 @@ public class BlobStoreManagerService extends SystemService {
} else {
blob.addOrReplaceCommitter(existingCommitter);
}
+ Slog.d(TAG, "Error committing the blob: " + session, e);
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
+ session.getOwnerUid(), session.getSessionId(), blob.getSize(),
+ FrameworkStatsLog.BLOB_COMMITTED__RESULT__ERROR_DURING_COMMIT);
session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
+ // If the commit fails and this blob data didn't exist before, delete it.
+ // But if it is a recommit, just leave it as is.
+ if (session.getSessionId() == blob.getBlobId()) {
+ deleteBlobLocked(blob);
+ userBlobs.remove(blob.getBlobHandle());
+ }
+ }
+ // Delete redundant data from recommits.
+ if (session.getSessionId() != blob.getBlobId()) {
+ deleteSessionLocked(session);
}
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
.remove(session.getSessionId());
@@ -779,8 +931,8 @@ public class BlobStoreManagerService extends SystemService {
blobMetadata.getBlobFile().delete();
} else {
addBlobForUserLocked(blobMetadata, blobMetadata.getUserId());
- blobMetadata.removeInvalidCommitters(userPackages);
- blobMetadata.removeInvalidLeasees(userPackages);
+ blobMetadata.removeCommittersFromUnknownPkgs(userPackages);
+ blobMetadata.removeLeaseesFromUnknownPkgs(userPackages);
}
mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId());
}
@@ -877,8 +1029,7 @@ public class BlobStoreManagerService extends SystemService {
userSessions.removeIf((sessionId, blobStoreSession) -> {
if (blobStoreSession.getOwnerUid() == uid
&& blobStoreSession.getOwnerPackageName().equals(packageName)) {
- blobStoreSession.getSessionFile().delete();
- mActiveBlobIds.remove(blobStoreSession.getSessionId());
+ deleteSessionLocked(blobStoreSession);
return true;
}
return false;
@@ -919,8 +1070,7 @@ public class BlobStoreManagerService extends SystemService {
if (userSessions != null) {
for (int i = 0, count = userSessions.size(); i < count; ++i) {
final BlobStoreSession session = userSessions.valueAt(i);
- session.getSessionFile().delete();
- mActiveBlobIds.remove(session.getSessionId());
+ deleteSessionLocked(session);
}
}
@@ -969,6 +1119,9 @@ public class BlobStoreManagerService extends SystemService {
userBlobs.entrySet().removeIf(entry -> {
final BlobMetadata blobMetadata = entry.getValue();
+ // Remove expired leases
+ blobMetadata.removeExpiredLeases();
+
if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
deleteBlobLocked(blobMetadata);
deletedBlobIds.add(blobMetadata.getBlobId());
@@ -996,28 +1149,41 @@ public class BlobStoreManagerService extends SystemService {
}
if (shouldRemove) {
- blobStoreSession.getSessionFile().delete();
- mActiveBlobIds.remove(blobStoreSession.getSessionId());
+ deleteSessionLocked(blobStoreSession);
deletedBlobIds.add(blobStoreSession.getSessionId());
}
return shouldRemove;
});
}
- if (LOGV) {
- Slog.v(TAG, "Completed idle maintenance; deleted "
- + Arrays.toString(deletedBlobIds.toArray()));
- }
+ Slog.d(TAG, "Completed idle maintenance; deleted "
+ + Arrays.toString(deletedBlobIds.toArray()));
writeBlobSessionsAsync();
}
+ @GuardedBy("mBlobsLock")
+ private void deleteSessionLocked(BlobStoreSession blobStoreSession) {
+ blobStoreSession.destroy();
+ mActiveBlobIds.remove(blobStoreSession.getSessionId());
+ }
+
@GuardedBy("mBlobsLock")
private void deleteBlobLocked(BlobMetadata blobMetadata) {
- blobMetadata.getBlobFile().delete();
+ blobMetadata.destroy();
mActiveBlobIds.remove(blobMetadata.getBlobId());
}
void runClearAllSessions(@UserIdInt int userId) {
synchronized (mBlobsLock) {
+ for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
+ final int sessionUserId = mSessions.keyAt(i);
+ if (userId != UserHandle.USER_ALL && userId != sessionUserId) {
+ continue;
+ }
+ final LongSparseArray userSessions = mSessions.valueAt(i);
+ for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
+ mActiveBlobIds.remove(userSessions.valueAt(j).getSessionId());
+ }
+ }
if (userId == UserHandle.USER_ALL) {
mSessions.clear();
} else {
@@ -1029,6 +1195,16 @@ public class BlobStoreManagerService extends SystemService {
void runClearAllBlobs(@UserIdInt int userId) {
synchronized (mBlobsLock) {
+ for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
+ final int blobUserId = mBlobsMap.keyAt(i);
+ if (userId != UserHandle.USER_ALL && userId != blobUserId) {
+ continue;
+ }
+ final ArrayMap userBlobs = mBlobsMap.valueAt(i);
+ for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
+ mActiveBlobIds.remove(userBlobs.valueAt(j).getBlobId());
+ }
+ }
if (userId == UserHandle.USER_ALL) {
mBlobsMap.clear();
} else {
@@ -1253,8 +1429,11 @@ public class BlobStoreManagerService extends SystemService {
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
- // TODO: Verify caller request is within limits (no. of calls/blob sessions/blobs)
- return createSessionInternal(blobHandle, callingUid, packageName);
+ try {
+ return createSessionInternal(blobHandle, callingUid, packageName);
+ } catch (LimitExceededException e) {
+ throw new ParcelableException(e);
+ }
}
@Override
@@ -1321,6 +1500,8 @@ public class BlobStoreManagerService extends SystemService {
"leaseExpiryTimeMillis must not be negative");
Objects.requireNonNull(packageName, "packageName must not be null");
+ description = BlobStoreConfig.getTruncatedLeaseDescription(description);
+
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
@@ -1488,7 +1669,7 @@ public class BlobStoreManagerService extends SystemService {
public int handleShellCommand(@NonNull ParcelFileDescriptor in,
@NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
@NonNull String[] args) {
- return (new BlobStoreManagerShellCommand(BlobStoreManagerService.this)).exec(this,
+ return new BlobStoreManagerShellCommand(BlobStoreManagerService.this).exec(this,
in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args);
}
}
@@ -1680,6 +1861,40 @@ public class BlobStoreManagerService extends SystemService {
}
}
+ private void registerBlobStorePuller() {
+ mStatsManager.setPullAtomCallback(
+ FrameworkStatsLog.BLOB_INFO,
+ null, // use default PullAtomMetadata values
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
+ );
+ }
+
+ private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
+ @Override
+ public int onPullAtom(int atomTag, List data) {
+ switch (atomTag) {
+ case FrameworkStatsLog.BLOB_INFO:
+ return pullBlobData(atomTag, data);
+ default:
+ throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
+ }
+ }
+ }
+
+ private int pullBlobData(int atomTag, List data) {
+ synchronized (mBlobsLock) {
+ for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
+ final ArrayMap userBlobs = mBlobsMap.valueAt(i);
+ for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
+ final BlobMetadata blob = userBlobs.valueAt(j);
+ data.add(blob.dumpAsStatsEvent(atomTag));
+ }
+ }
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
private class LocalService extends BlobStoreManagerInternal {
@Override
public void onIdleMaintenance() {
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 2b0458303a23e3265631e9e5b15be700a500f289..2f83be1e0370ad60b62e0c97aa565a517472b57b 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -27,10 +27,12 @@ import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_RDWR;
import static android.system.OsConstants.SEEK_SET;
+import static android.text.format.Formatter.FLAG_IEC_UNITS;
+import static android.text.format.Formatter.formatFileSize;
-import static com.android.server.blob.BlobStoreConfig.LOGV;
import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_SESSION_CREATION_TIME;
+import static com.android.server.blob.BlobStoreConfig.getMaxPermittedPackages;
import static com.android.server.blob.BlobStoreConfig.hasSessionExpired;
import android.annotation.BytesLong;
@@ -42,7 +44,9 @@ import android.app.blob.IBlobStoreSession;
import android.content.Context;
import android.os.Binder;
import android.os.FileUtils;
+import android.os.LimitExceededException;
import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
import android.os.Trace;
@@ -54,12 +58,15 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -72,7 +79,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
-/** TODO: add doc */
+/**
+ * Class to represent the state corresponding to an ongoing
+ * {@link android.app.blob.BlobStoreManager.Session}
+ */
@VisibleForTesting
class BlobStoreSession extends IBlobStoreSession.Stub {
@@ -98,7 +108,7 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
private File mSessionFile;
@GuardedBy("mRevocableFds")
- private ArrayList mRevocableFds = new ArrayList<>();
+ private final ArrayList mRevocableFds = new ArrayList<>();
// This will be accessed from only one thread at any point of time, so no need to grab
// a lock for this.
@@ -208,27 +218,37 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
throw new IllegalStateException("Not allowed to write in state: "
+ stateToString(mState));
}
+ }
- try {
- return openWriteLocked(offsetBytes, lengthBytes);
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
+ FileDescriptor fd = null;
+ try {
+ fd = openWriteInternal(offsetBytes, lengthBytes);
+ final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd);
+ synchronized (mSessionLock) {
+ if (mState != STATE_OPENED) {
+ IoUtils.closeQuietly(fd);
+ throw new IllegalStateException("Not allowed to write in state: "
+ + stateToString(mState));
+ }
+ trackRevocableFdLocked(revocableFd);
+ return revocableFd.getRevocableFileDescriptor();
}
+ } catch (IOException e) {
+ IoUtils.closeQuietly(fd);
+ throw ExceptionUtils.wrap(e);
}
}
- @GuardedBy("mSessionLock")
@NonNull
- private ParcelFileDescriptor openWriteLocked(@BytesLong long offsetBytes,
+ private FileDescriptor openWriteInternal(@BytesLong long offsetBytes,
@BytesLong long lengthBytes) throws IOException {
// TODO: Add limit on active open sessions/writes/reads
- FileDescriptor fd = null;
try {
final File sessionFile = getSessionFile();
if (sessionFile == null) {
throw new IllegalStateException("Couldn't get the file for this session");
}
- fd = Os.open(sessionFile.getPath(), O_CREAT | O_RDWR, 0600);
+ final FileDescriptor fd = Os.open(sessionFile.getPath(), O_CREAT | O_RDWR, 0600);
if (offsetBytes > 0) {
final long curOffset = Os.lseek(fd, offsetBytes, SEEK_SET);
if (curOffset != offsetBytes) {
@@ -239,10 +259,10 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
if (lengthBytes > 0) {
mContext.getSystemService(StorageManager.class).allocateBytes(fd, lengthBytes);
}
+ return fd;
} catch (ErrnoException e) {
- e.rethrowAsIOException();
+ throw e.rethrowAsIOException();
}
- return createRevocableFdLocked(fd);
}
@Override
@@ -254,29 +274,46 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
throw new IllegalStateException("Not allowed to read in state: "
+ stateToString(mState));
}
+ if (!BlobStoreConfig.shouldUseRevocableFdForReads()) {
+ try {
+ return new ParcelFileDescriptor(openReadInternal());
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+ }
- try {
- return openReadLocked();
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
+ FileDescriptor fd = null;
+ try {
+ fd = openReadInternal();
+ final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd);
+ synchronized (mSessionLock) {
+ if (mState != STATE_OPENED) {
+ IoUtils.closeQuietly(fd);
+ throw new IllegalStateException("Not allowed to read in state: "
+ + stateToString(mState));
+ }
+ trackRevocableFdLocked(revocableFd);
+ return revocableFd.getRevocableFileDescriptor();
}
+ } catch (IOException e) {
+ IoUtils.closeQuietly(fd);
+ throw ExceptionUtils.wrap(e);
}
}
- @GuardedBy("mSessionLock")
@NonNull
- private ParcelFileDescriptor openReadLocked() throws IOException {
- FileDescriptor fd = null;
+ private FileDescriptor openReadInternal() throws IOException {
try {
final File sessionFile = getSessionFile();
if (sessionFile == null) {
throw new IllegalStateException("Couldn't get the file for this session");
}
- fd = Os.open(sessionFile.getPath(), O_RDONLY, 0);
+ final FileDescriptor fd = Os.open(sessionFile.getPath(), O_RDONLY, 0);
+ return fd;
} catch (ErrnoException e) {
- e.rethrowAsIOException();
+ throw e.rethrowAsIOException();
}
- return createRevocableFdLocked(fd);
}
@Override
@@ -295,6 +332,11 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
throw new IllegalStateException("Not allowed to change access type in state: "
+ stateToString(mState));
}
+ if (mBlobAccessMode.getNumWhitelistedPackages() >= getMaxPermittedPackages()) {
+ throw new ParcelableException(new LimitExceededException(
+ "Too many packages permitted to access the blob: "
+ + mBlobAccessMode.getNumWhitelistedPackages()));
+ }
mBlobAccessMode.allowPackageAccess(packageName, certificate);
}
}
@@ -397,7 +439,7 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
}
mState = state;
- revokeAllFdsLocked();
+ revokeAllFds();
if (sendCallback) {
mListener.onStateChanged(this);
@@ -423,30 +465,36 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
mState = STATE_VERIFIED_VALID;
// Commit callback will be sent once the data is persisted.
} else {
- if (LOGV) {
- Slog.v(TAG, "Digest of the data didn't match the given BlobHandle.digest");
- }
+ Slog.d(TAG, "Digest of the data ("
+ + (mDataDigest == null ? "null" : BlobHandle.safeDigest(mDataDigest))
+ + ") didn't match the given BlobHandle.digest ("
+ + BlobHandle.safeDigest(mBlobHandle.digest) + ")");
mState = STATE_VERIFIED_INVALID;
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED, getOwnerUid(), mSessionId,
+ getSize(), FrameworkStatsLog.BLOB_COMMITTED__RESULT__DIGEST_MISMATCH);
sendCommitCallbackResult(COMMIT_RESULT_ERROR);
}
mListener.onStateChanged(this);
}
}
- @GuardedBy("mSessionLock")
- private void revokeAllFdsLocked() {
- for (int i = mRevocableFds.size() - 1; i >= 0; --i) {
- mRevocableFds.get(i).revoke();
+ void destroy() {
+ revokeAllFds();
+ getSessionFile().delete();
+ }
+
+ private void revokeAllFds() {
+ synchronized (mRevocableFds) {
+ for (int i = mRevocableFds.size() - 1; i >= 0; --i) {
+ mRevocableFds.get(i).revoke();
+ }
+ mRevocableFds.clear();
}
- mRevocableFds.clear();
}
@GuardedBy("mSessionLock")
- @NonNull
- private ParcelFileDescriptor createRevocableFdLocked(FileDescriptor fd)
- throws IOException {
- final RevocableFileDescriptor revocableFd =
- new RevocableFileDescriptor(mContext, fd);
+ private void trackRevocableFdLocked(RevocableFileDescriptor revocableFd) {
synchronized (mRevocableFds) {
mRevocableFds.add(revocableFd);
}
@@ -455,7 +503,6 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
mRevocableFds.remove(revocableFd);
}
});
- return revocableFd.getRevocableFileDescriptor();
}
@Nullable
@@ -510,6 +557,7 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
fout.println("ownerUid: " + mOwnerUid);
fout.println("ownerPkg: " + mOwnerPackageName);
fout.println("creation time: " + BlobStoreUtils.formatTime(mCreationTimeMs));
+ fout.println("size: " + formatFileSize(mContext, getSize(), FLAG_IEC_UNITS));
fout.println("blobHandle:");
fout.increaseIndent();
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 231263579088f19aacd7e16b0b031c4a1537afda..42725c51fd874df867ed076ed75605e118175139 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -59,9 +59,9 @@ import java.util.List;
*
*
Note: Beginning with API 30
* ({@link android.os.Build.VERSION_CODES#R}), JobScheduler will throttle runaway applications.
- * Calling {@link #schedule(JobInfo)} and other such methods with very high frequency is indicative
- * of an app bug and so, to make sure the system doesn't get overwhelmed, JobScheduler will begin
- * to throttle apps that show buggy behavior, regardless of target SDK version.
+ * Calling {@link #schedule(JobInfo)} and other such methods with very high frequency can have a
+ * high cost and so, to make sure the system doesn't get overwhelmed, JobScheduler will begin
+ * to throttle apps, regardless of target SDK version.
*/
@SystemService(Context.JOB_SCHEDULER_SERVICE)
public abstract class JobScheduler {
@@ -74,9 +74,16 @@ public abstract class JobScheduler {
public @interface Result {}
/**
- * Returned from {@link #schedule(JobInfo)} when an invalid parameter was supplied. This can occur
- * if the run-time for your job is too short, or perhaps the system can't resolve the
- * requisite {@link JobService} in your package.
+ * Returned from {@link #schedule(JobInfo)} if a job wasn't scheduled successfully. Scheduling
+ * can fail for a variety of reasons, including, but not limited to:
+ *
+ *
an invalid parameter was supplied (eg. the run-time for your job is too short, or the
+ * system can't resolve the requisite {@link JobService} in your package)
+ *
the app has too many jobs scheduled
+ *
the app has tried to schedule too many jobs in a short amount of time
+ *
+ * Attempting to schedule the job again immediately after receiving this result will not
+ * guarantee a successful schedule.
*/
public static final int RESULT_FAILURE = 0;
/**
@@ -89,6 +96,11 @@ public abstract class JobScheduler {
* ID with the new information in the {@link JobInfo}. If a job with the given ID is currently
* running, it will be stopped.
*
+ *
Note: Scheduling a job can have a high cost, even if it's
+ * rescheduling the same job and the job didn't execute, especially on platform versions before
+ * version {@link android.os.Build.VERSION_CODES#Q}. As such, the system may throttle calls to
+ * this API if calls are made too frequently in a short amount of time.
+ *
* @param job The job you wish scheduled. See
* {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs
* you can schedule.
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 887d82c6413f8e8beb21b6be711cd533d1b39f66..e15f0f37fc62ec4b4c54eaafadbbcb35ad5851a5 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -71,7 +71,7 @@ public interface AppStandbyInternal {
*/
void postOneTimeCheckIdleStates();
- void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId);
+ void reportEvent(UsageEvents.Event event, int userId);
void setLastJobRunTime(String packageName, int userId, long elapsedRealtime);
@@ -150,9 +150,7 @@ public interface AppStandbyInternal {
void clearCarrierPrivilegedApps();
- void flushToDisk(int userId);
-
- void flushDurationsToDisk();
+ void flushToDisk();
void initializeDefaultsForSystemApps(int userId);
@@ -162,7 +160,7 @@ public interface AppStandbyInternal {
void postReportExemptedSyncStart(String packageName, int userId);
- void dumpUser(IndentingPrintWriter idpw, int userId, List pkgs);
+ void dumpUsers(IndentingPrintWriter idpw, int[] userIds, List pkgs);
void dumpState(String[] args, PrintWriter pw);
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 e88865161dfa46bf0096bd8409b42708a6f31cdf..871e40fc9dfe518a1d8a335844cd320df85cd555 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -255,6 +255,18 @@ public class JobSchedulerService extends com.android.server.SystemService
private final CountQuotaTracker mQuotaTracker;
private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
+ private static final String QUOTA_TRACKER_SCHEDULE_LOGGED =
+ ".schedulePersisted out-of-quota logged";
+ private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED = new Category(
+ ".schedulePersisted()");
+ private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED = new Category(
+ ".schedulePersisted out-of-quota logged");
+ private static final Categorizer QUOTA_CATEGORIZER = (userId, packageName, tag) -> {
+ if (QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG.equals(tag)) {
+ return QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED;
+ }
+ return QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED;
+ };
/**
* Queue of pending jobs. The JobServiceContext class will receive jobs from this list
@@ -271,6 +283,7 @@ public class JobSchedulerService extends com.android.server.SystemService
ActivityManagerInternal mActivityManagerInternal;
IBatteryStats mBatteryStats;
DeviceIdleInternal mLocalDeviceIdleController;
+ @VisibleForTesting
AppStateTracker mAppStateTracker;
final UsageStatsManagerInternal mUsageStats;
private final AppStandbyInternal mAppStandbyInternal;
@@ -343,10 +356,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final StateController sc = mControllers.get(controller);
sc.onConstantsUpdatedLocked();
}
- mQuotaTracker.setEnabled(mConstants.ENABLE_API_QUOTAS);
- mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
- mConstants.API_QUOTA_SCHEDULE_COUNT,
- mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
+ updateQuotaTracker();
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
@@ -356,6 +366,14 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
+ @VisibleForTesting
+ void updateQuotaTracker() {
+ mQuotaTracker.setEnabled(mConstants.ENABLE_API_QUOTAS);
+ mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED,
+ mConstants.API_QUOTA_SCHEDULE_COUNT,
+ mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
+ }
+
static class MaxJobCounts {
private final KeyValueListParser.IntValue mTotal;
private final KeyValueListParser.IntValue mMaxBg;
@@ -508,6 +526,8 @@ public class JobSchedulerService extends com.android.server.SystemService
private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms";
private static final String KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION =
"aq_schedule_throw_exception";
+ private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
+ "aq_schedule_return_failure";
private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
@@ -521,6 +541,7 @@ public class JobSchedulerService extends com.android.server.SystemService
private static final int DEFAULT_API_QUOTA_SCHEDULE_COUNT = 250;
private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS;
private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true;
+ private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false;
/**
* Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -624,6 +645,11 @@ public class JobSchedulerService extends com.android.server.SystemService
*/
public boolean API_QUOTA_SCHEDULE_THROW_EXCEPTION =
DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION;
+ /**
+ * Whether or not to return a failure result when an app hits its schedule quota limit.
+ */
+ public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
+ DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -679,6 +705,9 @@ public class JobSchedulerService extends com.android.server.SystemService
API_QUOTA_SCHEDULE_THROW_EXCEPTION = mParser.getBoolean(
KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION);
+ API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = mParser.getBoolean(
+ KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
+ DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT);
}
void dump(IndentingPrintWriter pw) {
@@ -713,6 +742,8 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.printPair(KEY_API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS).println();
pw.printPair(KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
API_QUOTA_SCHEDULE_THROW_EXCEPTION).println();
+ pw.printPair(KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
+ API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT).println();
pw.decreaseIndent();
}
@@ -741,6 +772,8 @@ public class JobSchedulerService extends com.android.server.SystemService
proto.write(ConstantsProto.API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS);
proto.write(ConstantsProto.API_QUOTA_SCHEDULE_THROW_EXCEPTION,
API_QUOTA_SCHEDULE_THROW_EXCEPTION);
+ proto.write(ConstantsProto.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
+ API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT);
}
}
@@ -974,12 +1007,17 @@ public class JobSchedulerService extends com.android.server.SystemService
public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
int userId, String tag) {
- if (job.isPersisted()) {
- // Only limit schedule calls for persisted jobs.
+ final String servicePkg = job.getService().getPackageName();
+ if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) {
+ // Only limit schedule calls for persisted jobs scheduled by the app itself.
final String pkg =
packageName == null ? job.getService().getPackageName() : packageName;
if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
- Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
+ if (mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED)) {
+ // Don't log too frequently
+ Slog.wtf(TAG, userId + "-" + pkg + " has called schedule() too many times");
+ mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED);
+ }
mAppStandbyInternal.restrictApp(
pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) {
@@ -1005,13 +1043,17 @@ public class JobSchedulerService extends com.android.server.SystemService
// Only throw the exception for debuggable apps.
throw new LimitExceededException(
"schedule()/enqueue() called more than "
- + mQuotaTracker.getLimit(Category.SINGLE_CATEGORY)
+ + mQuotaTracker.getLimit(
+ QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED)
+ " times in the past "
- + mQuotaTracker.getWindowSizeMs(Category.SINGLE_CATEGORY)
- + "ms");
+ + mQuotaTracker.getWindowSizeMs(
+ QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED)
+ + "ms. See the documentation for more information.");
}
}
- return JobScheduler.RESULT_FAILURE;
+ if (mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT) {
+ return JobScheduler.RESULT_FAILURE;
+ }
}
mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG);
}
@@ -1372,10 +1414,12 @@ public class JobSchedulerService extends com.android.server.SystemService
// Set up the app standby bucketing tracker
mStandbyTracker = new StandbyTracker();
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
- mQuotaTracker = new CountQuotaTracker(context, Categorizer.SINGLE_CATEGORIZER);
- mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
+ mQuotaTracker = new CountQuotaTracker(context, QUOTA_CATEGORIZER);
+ mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED,
mConstants.API_QUOTA_SCHEDULE_COUNT,
mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
+ // Log at most once per minute.
+ mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED, 1, 60_000);
mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
mAppStandbyInternal.addListener(mStandbyTracker);
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 372ec981df02a2af8f62a83395bb9f4b455a8dfa..70155ee8472077d577b5ecec072e6ba85080a5cd 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -675,6 +675,14 @@ public class AppIdleHistory {
return Long.parseLong(value);
}
+
+ public void writeAppIdleTimes() {
+ final int size = mIdleHistory.size();
+ for (int i = 0; i < size; i++) {
+ writeAppIdleTimes(mIdleHistory.keyAt(i));
+ }
+ }
+
public void writeAppIdleTimes(int userId) {
FileOutputStream fos = null;
AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
@@ -743,8 +751,18 @@ public class AppIdleHistory {
}
}
- public void dump(IndentingPrintWriter idpw, int userId, List pkgs) {
- idpw.println("App Standby States:");
+ public void dumpUsers(IndentingPrintWriter idpw, int[] userIds, List pkgs) {
+ final int numUsers = userIds.length;
+ for (int i = 0; i < numUsers; i++) {
+ idpw.println();
+ dumpUser(idpw, userIds[i], pkgs);
+ }
+ }
+
+ private void dumpUser(IndentingPrintWriter idpw, int userId, List pkgs) {
+ idpw.print("User ");
+ idpw.print(userId);
+ idpw.println(" App Standby States:");
idpw.increaseIndent();
ArrayMap userHistory = mIdleHistory.get(userId);
final long elapsedRealtime = SystemClock.elapsedRealtime();
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 280a6870a5e1b52d53e658064299bbb842d65d0d..f36084386f486c22bedd7ea428d23e59193becd4 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -82,18 +82,18 @@ import android.os.BatteryStats;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
+import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
-import android.os.PowerWhitelistManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.telephony.TelephonyManager;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Slog;
@@ -205,6 +205,10 @@ public class AppStandbyController implements AppStandbyInternal {
*/
private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
+ private static final int HEADLESS_APP_CHECK_FLAGS =
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS;
+
// To name the lock for stack traces
static class Lock {}
@@ -233,11 +237,19 @@ public class AppStandbyController implements AppStandbyInternal {
* Set of system apps that are headless (don't have any declared activities, enabled or
* disabled). Presence in this map indicates that the app is a headless system app.
*/
- @GuardedBy("mAppIdleLock")
- private final ArrayMap mHeadlessSystemApps = new ArrayMap<>();
+ @GuardedBy("mHeadlessSystemApps")
+ private final ArraySet mHeadlessSystemApps = new ArraySet<>();
private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+ // Cache the active network scorer queried from the network scorer service
+ private volatile String mCachedNetworkScorer = null;
+ // The last time the network scorer service was queried
+ private volatile long mCachedNetworkScorerAtMillis = 0L;
+ // How long before querying the network scorer again. During this time, subsequent queries will
+ // get the cached value
+ private static final long NETWORK_SCORER_CACHE_DURATION_MILLIS = 5000L;
+
// Messages for the handler
static final int MSG_INFORM_LISTENERS = 3;
static final int MSG_FORCE_IDLE_STATE = 4;
@@ -387,6 +399,7 @@ public class AppStandbyController implements AppStandbyInternal {
DeviceStateReceiver deviceStateReceiver = new DeviceStateReceiver();
IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
+ deviceStates.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
mContext.registerReceiver(deviceStateReceiver, deviceStates);
synchronized (mAppIdleLock) {
@@ -442,13 +455,14 @@ public class AppStandbyController implements AppStandbyInternal {
mSystemServicesReady = true;
+ // Offload to handler thread to avoid boot time impact.
+ mHandler.post(mInjector::updatePowerWhitelistCache);
+
boolean userFileExists;
synchronized (mAppIdleLock) {
userFileExists = mAppIdleHistory.userFileExists(UserHandle.USER_SYSTEM);
}
- loadHeadlessSystemAppCache();
-
if (mPendingInitializeDefaults || !userFileExists) {
initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
}
@@ -458,6 +472,10 @@ public class AppStandbyController implements AppStandbyInternal {
}
} else if (phase == PHASE_BOOT_COMPLETED) {
setChargingState(mInjector.isCharging());
+
+ // Offload to handler thread after boot completed to avoid boot time impact. This means
+ // that headless system apps may be put in a lower bucket until boot has completed.
+ mHandler.post(this::loadHeadlessSystemAppCache);
}
}
@@ -849,7 +867,7 @@ public class AppStandbyController implements AppStandbyInternal {
}
@Override
- public void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
+ public void reportEvent(UsageEvents.Event event, int userId) {
if (!mAppIdleEnabled) return;
final int eventType = event.getEventType();
if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED
@@ -863,6 +881,7 @@ public class AppStandbyController implements AppStandbyInternal {
final String pkg = event.getPackageName();
final List linkedProfiles = getCrossProfileTargets(pkg, userId);
synchronized (mAppIdleLock) {
+ final long elapsedRealtime = mInjector.elapsedRealtime();
reportEventLocked(pkg, eventType, elapsedRealtime, userId);
final int size = linkedProfiles.size();
@@ -1079,15 +1098,11 @@ public class AppStandbyController implements AppStandbyInternal {
return STANDBY_BUCKET_EXEMPTED;
}
if (mSystemServicesReady) {
- try {
- // We allow all whitelisted apps, including those that don't want to be whitelisted
- // for idle mode, because app idle (aka app standby) is really not as big an issue
- // for controlling who participates vs. doze mode.
- if (mInjector.isNonIdleWhitelisted(packageName)) {
- return STANDBY_BUCKET_EXEMPTED;
- }
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
+ // We allow all whitelisted apps, including those that don't want to be whitelisted
+ // for idle mode, because app idle (aka app standby) is really not as big an issue
+ // for controlling who participates vs. doze mode.
+ if (mInjector.isNonIdleWhitelisted(packageName)) {
+ return STANDBY_BUCKET_EXEMPTED;
}
if (isActiveDeviceAdmin(packageName, userId)) {
@@ -1121,7 +1136,9 @@ public class AppStandbyController implements AppStandbyInternal {
}
private boolean isHeadlessSystemApp(String packageName) {
- return mHeadlessSystemApps.containsKey(packageName);
+ synchronized (mHeadlessSystemApps) {
+ return mHeadlessSystemApps.contains(packageName);
+ }
}
@Override
@@ -1154,6 +1171,8 @@ public class AppStandbyController implements AppStandbyInternal {
return new int[0];
}
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getIdleUidsForUser");
+
final long elapsedRealtime = mInjector.elapsedRealtime();
List apps;
@@ -1189,6 +1208,7 @@ public class AppStandbyController implements AppStandbyInternal {
uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
}
}
+
if (DEBUG) {
Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime));
}
@@ -1210,6 +1230,8 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
return res;
}
@@ -1576,8 +1598,16 @@ public class AppStandbyController implements AppStandbyInternal {
}
private boolean isActiveNetworkScorer(String packageName) {
- String activeScorer = mInjector.getActiveNetworkScorer();
- return packageName != null && packageName.equals(activeScorer);
+ // Validity of network scorer cache is limited to a few seconds. Fetch it again
+ // if longer since query.
+ // This is a temporary optimization until there's a callback mechanism for changes to network scorer.
+ final long now = SystemClock.elapsedRealtime();
+ if (mCachedNetworkScorer == null
+ || mCachedNetworkScorerAtMillis < now - NETWORK_SCORER_CACHE_DURATION_MILLIS) {
+ mCachedNetworkScorer = mInjector.getActiveNetworkScorer();
+ mCachedNetworkScorerAtMillis = now;
+ }
+ return packageName != null && packageName.equals(mCachedNetworkScorer);
}
private void informListeners(String packageName, int userId, int bucket, int reason,
@@ -1602,18 +1632,11 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
- @Override
- public void flushToDisk(int userId) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.writeAppIdleTimes(userId);
- }
- }
@Override
- public void flushDurationsToDisk() {
- // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
- // considered not-idle, which is the safest outcome in such an event.
+ public void flushToDisk() {
synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleTimes();
mAppIdleHistory.writeAppIdleDurations();
}
}
@@ -1692,24 +1715,29 @@ public class AppStandbyController implements AppStandbyInternal {
return;
}
try {
- PackageInfo pi = mPackageManager.getPackageInfoAsUser(packageName,
- PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS,
- userId);
+ PackageInfo pi = mPackageManager.getPackageInfoAsUser(
+ packageName, HEADLESS_APP_CHECK_FLAGS, userId);
evaluateSystemAppException(pi);
} catch (PackageManager.NameNotFoundException e) {
- mHeadlessSystemApps.remove(packageName);
+ synchronized (mHeadlessSystemApps) {
+ mHeadlessSystemApps.remove(packageName);
+ }
}
}
- private void evaluateSystemAppException(@Nullable PackageInfo pkgInfo) {
- if (pkgInfo.applicationInfo != null && pkgInfo.applicationInfo.isSystemApp()) {
- synchronized (mAppIdleLock) {
- if (pkgInfo.activities == null || pkgInfo.activities.length == 0) {
- // Headless system app.
- mHeadlessSystemApps.put(pkgInfo.packageName, true);
- } else {
- mHeadlessSystemApps.remove(pkgInfo.packageName);
- }
+ /** Returns true if the exception status changed. */
+ private boolean evaluateSystemAppException(@Nullable PackageInfo pkgInfo) {
+ if (pkgInfo == null || pkgInfo.applicationInfo == null
+ || (!pkgInfo.applicationInfo.isSystemApp()
+ && !pkgInfo.applicationInfo.isUpdatedSystemApp())) {
+ return false;
+ }
+ synchronized (mHeadlessSystemApps) {
+ if (pkgInfo.activities == null || pkgInfo.activities.length == 0) {
+ // Headless system app.
+ return mHeadlessSystemApps.add(pkgInfo.packageName);
+ } else {
+ return mHeadlessSystemApps.remove(pkgInfo.packageName);
}
}
}
@@ -1746,15 +1774,19 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
- /** Call on a system update to temporarily reset system app buckets. */
+ /** Call on system boot to get the initial set of headless system apps. */
private void loadHeadlessSystemAppCache() {
Slog.d(TAG, "Loading headless system app cache. appIdleEnabled=" + mAppIdleEnabled);
final List packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS,
- UserHandle.USER_SYSTEM);
+ HEADLESS_APP_CHECK_FLAGS, UserHandle.USER_SYSTEM);
final int packageCount = packages.size();
for (int i = 0; i < packageCount; i++) {
- evaluateSystemAppException(packages.get(i));
+ PackageInfo pkgInfo = packages.get(i);
+ if (pkgInfo != null && evaluateSystemAppException(pkgInfo)) {
+ mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE,
+ UserHandle.USER_SYSTEM, -1, pkgInfo.packageName)
+ .sendToTarget();
+ }
}
}
@@ -1781,9 +1813,9 @@ public class AppStandbyController implements AppStandbyInternal {
}
@Override
- public void dumpUser(IndentingPrintWriter idpw, int userId, List pkgs) {
+ public void dumpUsers(IndentingPrintWriter idpw, int[] userIds, List pkgs) {
synchronized (mAppIdleLock) {
- mAppIdleHistory.dump(idpw, userId, pkgs);
+ mAppIdleHistory.dumpUsers(idpw, userIds, pkgs);
}
}
@@ -1794,8 +1826,6 @@ public class AppStandbyController implements AppStandbyInternal {
+ "): " + mCarrierPrivilegedApps);
}
- final long now = System.currentTimeMillis();
-
pw.println();
pw.println("Settings:");
@@ -1852,12 +1882,17 @@ public class AppStandbyController implements AppStandbyInternal {
pw.println();
pw.println("mHeadlessSystemApps=[");
- for (int i = mHeadlessSystemApps.size() - 1; i >= 0; --i) {
- pw.print(mHeadlessSystemApps.keyAt(i));
- pw.println(",");
+ synchronized (mHeadlessSystemApps) {
+ for (int i = mHeadlessSystemApps.size() - 1; i >= 0; --i) {
+ pw.print(" ");
+ pw.print(mHeadlessSystemApps.valueAt(i));
+ pw.println(",");
+ }
}
pw.println("]");
pw.println();
+
+ mInjector.dump(pw);
}
/**
@@ -1874,7 +1909,7 @@ public class AppStandbyController implements AppStandbyInternal {
private PackageManagerInternal mPackageManagerInternal;
private DisplayManager mDisplayManager;
private PowerManager mPowerManager;
- private PowerWhitelistManager mPowerWhitelistManager;
+ private IDeviceIdleController mDeviceIdleController;
private CrossProfileAppsInternal mCrossProfileAppsInternal;
int mBootPhase;
/**
@@ -1882,6 +1917,11 @@ public class AppStandbyController implements AppStandbyInternal {
* automatically placed in the RESTRICTED bucket.
*/
long mAutoRestrictedBucketDelayMs = ONE_DAY;
+ /**
+ * Cached set of apps that are power whitelisted, including those not whitelisted from idle.
+ */
+ @GuardedBy("mPowerWhitelistedApps")
+ private final ArraySet mPowerWhitelistedApps = new ArraySet<>();
Injector(Context context, Looper looper) {
mContext = context;
@@ -1898,7 +1938,8 @@ public class AppStandbyController implements AppStandbyInternal {
void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
- mPowerWhitelistManager = mContext.getSystemService(PowerWhitelistManager.class);
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
mBatteryStats = IBatteryStats.Stub.asInterface(
ServiceManager.getService(BatteryStats.SERVICE_NAME));
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
@@ -1949,8 +1990,34 @@ public class AppStandbyController implements AppStandbyInternal {
return mBatteryManager.isCharging();
}
- boolean isNonIdleWhitelisted(String packageName) throws RemoteException {
- return mPowerWhitelistManager.isWhitelisted(packageName, false);
+ boolean isNonIdleWhitelisted(String packageName) {
+ if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) {
+ return false;
+ }
+ synchronized (mPowerWhitelistedApps) {
+ return mPowerWhitelistedApps.contains(packageName);
+ }
+ }
+
+ private void updatePowerWhitelistCache() {
+ if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) {
+ return;
+ }
+ try {
+ // Don't call out to DeviceIdleController with the lock held.
+ final String[] whitelistedPkgs =
+ mDeviceIdleController.getFullPowerWhitelistExceptIdle();
+ synchronized (mPowerWhitelistedApps) {
+ mPowerWhitelistedApps.clear();
+ final int len = whitelistedPkgs.length;
+ for (int i = 0; i < len; ++i) {
+ mPowerWhitelistedApps.add(whitelistedPkgs[i]);
+ }
+ }
+ } catch (RemoteException e) {
+ // Should not happen.
+ Slog.wtf(TAG, "Failed to get power whitelist", e);
+ }
}
boolean isRestrictedBucketEnabled() {
@@ -2037,6 +2104,19 @@ public class AppStandbyController implements AppStandbyInternal {
}
return mCrossProfileAppsInternal.getTargetUserProfiles(pkg, userId);
}
+
+ void dump(PrintWriter pw) {
+ pw.println("mPowerWhitelistedApps=[");
+ synchronized (mPowerWhitelistedApps) {
+ for (int i = mPowerWhitelistedApps.size() - 1; i >= 0; --i) {
+ pw.print(" ");
+ pw.print(mPowerWhitelistedApps.valueAt(i));
+ pw.println(",");
+ }
+ }
+ pw.println("]");
+ pw.println();
+ }
}
class AppStandbyHandler extends Handler {
@@ -2122,6 +2202,11 @@ public class AppStandbyController implements AppStandbyInternal {
case BatteryManager.ACTION_DISCHARGING:
setChargingState(false);
break;
+ case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
+ if (mSystemServicesReady) {
+ mHandler.post(mInjector::updatePowerWhitelistCache);
+ }
+ break;
}
}
}
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index ac501a510e80ee16b4c9d8d952f776ea9a13d9b9..4417b681efc370863ae0a520784e7f6f028c2e05 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -96,11 +96,6 @@ java_sdk_library {
":updatable-media-srcs",
],
- // TODO(b/155480189) - Remove naming_scheme once references have been resolved.
- // Temporary java_sdk_library component naming scheme to use to ease the transition from separate
- // modules to java_sdk_library.
- naming_scheme: "framework-modules",
-
libs: [
"framework_media_annotation",
],
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 070b13b9e5928f7a98ef6944b40b5f8df7608468..e4b5d19e67c97313192fea9febb2b935facb1660 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -31,6 +31,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
+import com.google.android.exoplayer2.extractor.ChunkIndex;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
@@ -203,6 +204,15 @@ public final class MediaParser {
/** Returned by {@link #getDurationMicros()} when the duration is unknown. */
public static final int UNKNOWN_DURATION = Integer.MIN_VALUE;
+ /**
+ * For each {@link #getSeekPoints} call, returns a single {@link SeekPoint} whose {@link
+ * SeekPoint#timeMicros} matches the requested timestamp, and whose {@link
+ * SeekPoint#position} is 0.
+ *
+ * @hide
+ */
+ public static final SeekMap DUMMY = new SeekMap(new DummyExoPlayerSeekMap());
+
private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
private SeekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
@@ -219,7 +229,8 @@ public final class MediaParser {
* duration is unknown.
*/
public long getDurationMicros() {
- return mExoPlayerSeekMap.getDurationUs();
+ long durationUs = mExoPlayerSeekMap.getDurationUs();
+ return durationUs != C.TIME_UNSET ? durationUs : UNKNOWN_DURATION;
}
/**
@@ -794,6 +805,79 @@ public final class MediaParser {
*/
public static final String PARAMETER_EAGERLY_EXPOSE_TRACKTYPE =
"android.media.mediaparser.eagerlyExposeTrackType";
+ /**
+ * Sets whether a dummy {@link SeekMap} should be exposed before starting extraction. {@code
+ * boolean} expected. Default value is {@code false}.
+ *
+ *
For each {@link SeekMap#getSeekPoints} call, the dummy {@link SeekMap} returns a single
+ * {@link SeekPoint} whose {@link SeekPoint#timeMicros} matches the requested timestamp, and
+ * whose {@link SeekPoint#position} is 0.
+ *
+ * @hide
+ */
+ public static final String PARAMETER_EXPOSE_DUMMY_SEEKMAP =
+ "android.media.mediaparser.exposeDummySeekMap";
+
+ /**
+ * Sets whether chunk indices available in the extracted media should be exposed as {@link
+ * MediaFormat MediaFormats}. {@code boolean} expected. Default value is {@link false}.
+ *
+ *
When set to true, any information about media segmentation will be exposed as a {@link
+ * MediaFormat} (with track index 0) containing four {@link ByteBuffer} elements under the
+ * following keys:
+ *
+ *
+ *
"chunk-index-int-sizes": Contains {@code ints} representing the sizes in bytes of each
+ * of the media segments.
+ *
"chunk-index-long-offsets": Contains {@code longs} representing the byte offsets of
+ * each segment in the stream.
+ *
"chunk-index-long-us-durations": Contains {@code longs} representing the media duration
+ * of each segment, in microseconds.
+ *
"chunk-index-long-us-times": Contains {@code longs} representing the start time of each
+ * segment, in microseconds.
+ *
+ *
+ * @hide
+ */
+ public static final String PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT =
+ "android.media.mediaParser.exposeChunkIndexAsMediaFormat";
+ /**
+ * Sets a list of closed-caption {@link MediaFormat MediaFormats} that should be exposed as part
+ * of the extracted media. {@code List} expected. Default value is an empty list.
+ *
+ *
Expected keys in the {@link MediaFormat} are:
+ *
+ *
+ *
{@link MediaFormat#KEY_MIME}: Determine the type of captions (for example,
+ * application/cea-608). Mandatory.
+ *
{@link MediaFormat#KEY_CAPTION_SERVICE_NUMBER}: Determine the channel on which the
+ * captions are transmitted. Optional.
+ *
+ *
+ * @hide
+ */
+ public static final String PARAMETER_EXPOSE_CAPTION_FORMATS =
+ "android.media.mediaParser.exposeCaptionFormats";
+ /**
+ * Sets whether the value associated with {@link #PARAMETER_EXPOSE_CAPTION_FORMATS} should
+ * override any in-band caption service declarations. {@code boolean} expected. Default value is
+ * {@link false}.
+ *
+ *
When {@code false}, any present in-band caption services information will override the
+ * values associated with {@link #PARAMETER_EXPOSE_CAPTION_FORMATS}.
+ *
+ * @hide
+ */
+ public static final String PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS =
+ "android.media.mediaParser.overrideInBandCaptionDeclarations";
+ /**
+ * Sets whether a track for EMSG events should be exposed in case of parsing a container that
+ * supports them. {@code boolean} expected. Default value is {@link false}.
+ *
+ * @hide
+ */
+ public static final String PARAMETER_EXPOSE_EMSG_TRACK =
+ "android.media.mediaParser.exposeEmsgTrack";
// Private constants.
@@ -804,6 +888,7 @@ public final class MediaParser {
private static final String TS_MODE_MULTI_PMT = "multi_pmt";
private static final String TS_MODE_HLS = "hls";
private static final int BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY = 6;
+ private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
@IntDef(
value = {
@@ -953,10 +1038,13 @@ public final class MediaParser {
private final DataReaderAdapter mScratchDataReaderAdapter;
private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
@Nullable private final Constructor mSchemeInitDataConstructor;
+ private final ArrayList mMuxedCaptionFormats;
private boolean mInBandCryptoInfo;
private boolean mIncludeSupplementalData;
private boolean mIgnoreTimestampOffset;
private boolean mEagerlyExposeTrackType;
+ private boolean mExposeDummySeekMap;
+ private boolean mExposeChunkIndexAsMediaFormat;
private String mParserName;
private Extractor mExtractor;
private ExtractorInput mExtractorInput;
@@ -1016,6 +1104,15 @@ public final class MediaParser {
if (PARAMETER_EAGERLY_EXPOSE_TRACKTYPE.equals(parameterName)) {
mEagerlyExposeTrackType = (boolean) value;
}
+ if (PARAMETER_EXPOSE_DUMMY_SEEKMAP.equals(parameterName)) {
+ mExposeDummySeekMap = (boolean) value;
+ }
+ if (PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT.equals(parameterName)) {
+ mExposeChunkIndexAsMediaFormat = (boolean) value;
+ }
+ if (PARAMETER_EXPOSE_CAPTION_FORMATS.equals(parameterName)) {
+ setMuxedCaptionFormats((List) value);
+ }
mParserParameters.put(parameterName, value);
return this;
}
@@ -1054,8 +1151,8 @@ public final class MediaParser {
*
*
This method will block until some progress has been made.
*
- *
If this instance was created using {@link #create}. the first call to this method will
- * sniff the content with the parsers with the provided names.
+ *
If this instance was created using {@link #create}, the first call to this method will
+ * sniff the content using the selected parser implementations.
*
* @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media
* container data.
@@ -1077,11 +1174,10 @@ public final class MediaParser {
}
mExoDataReader.mInputReader = seekableInputReader;
- // TODO: Apply parameters when creating extractor instances.
if (mExtractor == null) {
+ mPendingExtractorInit = true;
if (!mParserName.equals(PARSER_NAME_UNKNOWN)) {
mExtractor = createExtractor(mParserName);
- mExtractor.init(new ExtractorOutputAdapter());
} else {
for (String parserName : mParserNamesPool) {
Extractor extractor = createExtractor(parserName);
@@ -1106,9 +1202,18 @@ public final class MediaParser {
}
if (mPendingExtractorInit) {
+ if (mExposeDummySeekMap) {
+ // We propagate the dummy seek map before initializing the extractor, in case the
+ // extractor initialization outputs a seek map.
+ mOutputConsumer.onSeekMapFound(SeekMap.DUMMY);
+ }
mExtractor.init(new ExtractorOutputAdapter());
mPendingExtractorInit = false;
+ // We return after initialization to allow clients use any output information before
+ // starting actual extraction.
+ return true;
}
+
if (isPendingSeek()) {
mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeMicros);
removePendingSeek();
@@ -1122,6 +1227,7 @@ public final class MediaParser {
throw new ParsingException(e);
}
if (result == Extractor.RESULT_END_OF_INPUT) {
+ mExtractorInput = null;
return false;
}
if (result == Extractor.RESULT_SEEK) {
@@ -1179,6 +1285,14 @@ public final class MediaParser {
mScratchDataReaderAdapter = new DataReaderAdapter();
mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter();
mSchemeInitDataConstructor = getSchemeInitDataConstructor();
+ mMuxedCaptionFormats = new ArrayList<>();
+ }
+
+ private void setMuxedCaptionFormats(List mediaFormats) {
+ mMuxedCaptionFormats.clear();
+ for (MediaFormat mediaFormat : mediaFormats) {
+ mMuxedCaptionFormats.add(toExoPlayerCaptionFormat(mediaFormat));
+ }
}
private boolean isPendingSeek() {
@@ -1204,6 +1318,10 @@ public final class MediaParser {
: 0;
return new MatroskaExtractor(flags);
case PARSER_NAME_FMP4:
+ flags |=
+ getBooleanParameter(PARAMETER_EXPOSE_EMSG_TRACK)
+ ? FragmentedMp4Extractor.FLAG_ENABLE_EMSG_TRACK
+ : 0;
flags |=
getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
@@ -1217,7 +1335,11 @@ public final class MediaParser {
? FragmentedMp4Extractor
.FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME
: 0;
- return new FragmentedMp4Extractor(flags, timestampAdjuster);
+ return new FragmentedMp4Extractor(
+ flags,
+ timestampAdjuster,
+ /* sideloadedTrack= */ null,
+ mMuxedCaptionFormats);
case PARSER_NAME_MP4:
flags |=
getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
@@ -1268,6 +1390,10 @@ public final class MediaParser {
getBooleanParameter(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM)
? DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM
: 0;
+ flags |=
+ getBooleanParameter(PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS)
+ ? DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS
+ : 0;
String tsMode = getStringParameter(PARAMETER_TS_MODE, TS_MODE_SINGLE_PMT);
int hlsMode =
TS_MODE_SINGLE_PMT.equals(tsMode)
@@ -1280,7 +1406,7 @@ public final class MediaParser {
timestampAdjuster != null
? timestampAdjuster
: new TimestampAdjuster(/* firstSampleTimestampUs= */ 0),
- new DefaultTsPayloadReaderFactory(flags));
+ new DefaultTsPayloadReaderFactory(flags, mMuxedCaptionFormats));
case PARSER_NAME_FLV:
return new FlvExtractor();
case PARSER_NAME_OGG:
@@ -1402,6 +1528,19 @@ public final class MediaParser {
@Override
public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
+ if (mExposeChunkIndexAsMediaFormat && exoplayerSeekMap instanceof ChunkIndex) {
+ ChunkIndex chunkIndex = (ChunkIndex) exoplayerSeekMap;
+ MediaFormat mediaFormat = new MediaFormat();
+ mediaFormat.setByteBuffer("chunk-index-int-sizes", toByteBuffer(chunkIndex.sizes));
+ mediaFormat.setByteBuffer(
+ "chunk-index-long-offsets", toByteBuffer(chunkIndex.offsets));
+ mediaFormat.setByteBuffer(
+ "chunk-index-long-us-durations", toByteBuffer(chunkIndex.durationsUs));
+ mediaFormat.setByteBuffer(
+ "chunk-index-long-us-times", toByteBuffer(chunkIndex.timesUs));
+ mOutputConsumer.onTrackDataFound(
+ /* trackIndex= */ 0, new TrackData(mediaFormat, /* drmInitData= */ null));
+ }
mOutputConsumer.onSeekMapFound(new SeekMap(exoplayerSeekMap));
}
}
@@ -1549,6 +1688,9 @@ public final class MediaParser {
if (cryptoData != mLastReceivedCryptoData) {
mLastOutputCryptoInfo =
createNewCryptoInfoAndPopulateWithCryptoData(cryptoData);
+ // We are using in-band crypto info, so the IV will be ignored. But we prevent
+ // it from being null because toString assumes it non-null.
+ mLastOutputCryptoInfo.iv = EMPTY_BYTE_ARRAY;
}
} else /* We must populate the full CryptoInfo. */ {
// CryptoInfo.pattern is not accessible to the user, so the user needs to feed
@@ -1682,6 +1824,28 @@ public final class MediaParser {
}
}
+ private static final class DummyExoPlayerSeekMap
+ implements com.google.android.exoplayer2.extractor.SeekMap {
+
+ @Override
+ public boolean isSeekable() {
+ return true;
+ }
+
+ @Override
+ public long getDurationUs() {
+ return C.TIME_UNSET;
+ }
+
+ @Override
+ public SeekPoints getSeekPoints(long timeUs) {
+ com.google.android.exoplayer2.extractor.SeekPoint seekPoint =
+ new com.google.android.exoplayer2.extractor.SeekPoint(
+ timeUs, /* position= */ 0);
+ return new SeekPoints(seekPoint, seekPoint);
+ }
+ }
+
/** Creates extractor instances. */
private interface ExtractorFactory {
@@ -1691,6 +1855,16 @@ public final class MediaParser {
// Private static methods.
+ private static Format toExoPlayerCaptionFormat(MediaFormat mediaFormat) {
+ Format.Builder formatBuilder =
+ new Format.Builder().setSampleMimeType(mediaFormat.getString(MediaFormat.KEY_MIME));
+ if (mediaFormat.containsKey(MediaFormat.KEY_CAPTION_SERVICE_NUMBER)) {
+ formatBuilder.setAccessibilityChannel(
+ mediaFormat.getInteger(MediaFormat.KEY_CAPTION_SERVICE_NUMBER));
+ }
+ return formatBuilder.build();
+ }
+
private static MediaFormat toMediaFormat(Format format) {
MediaFormat result = new MediaFormat();
setOptionalMediaFormatInt(result, MediaFormat.KEY_BIT_RATE, format.bitrate);
@@ -1759,14 +1933,34 @@ public final class MediaParser {
// format for convenient use from ExoPlayer.
result.setString("crypto-mode-fourcc", format.drmInitData.schemeType);
}
+ if (format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) {
+ result.setLong("subsample-offset-us-long", format.subsampleOffsetUs);
+ }
// LACK OF SUPPORT FOR:
- // format.containerMimeType;
// format.id;
// format.metadata;
// format.stereoMode;
return result;
}
+ private static ByteBuffer toByteBuffer(long[] longArray) {
+ ByteBuffer byteBuffer = ByteBuffer.allocateDirect(longArray.length * Long.BYTES);
+ for (long element : longArray) {
+ byteBuffer.putLong(element);
+ }
+ byteBuffer.flip();
+ return byteBuffer;
+ }
+
+ private static ByteBuffer toByteBuffer(int[] intArray) {
+ ByteBuffer byteBuffer = ByteBuffer.allocateDirect(intArray.length * Integer.BYTES);
+ for (int element : intArray) {
+ byteBuffer.putInt(element);
+ }
+ byteBuffer.flip();
+ return byteBuffer;
+ }
+
private static String toTypeString(int type) {
switch (type) {
case C.TRACK_TYPE_VIDEO:
@@ -1922,6 +2116,15 @@ public final class MediaParser {
expectedTypeByParameterName.put(PARAMETER_INCLUDE_SUPPLEMENTAL_DATA, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_IGNORE_TIMESTAMP_OFFSET, Boolean.class);
expectedTypeByParameterName.put(PARAMETER_EAGERLY_EXPOSE_TRACKTYPE, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_EXPOSE_DUMMY_SEEKMAP, Boolean.class);
+ expectedTypeByParameterName.put(
+ PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT, Boolean.class);
+ expectedTypeByParameterName.put(
+ PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_EXPOSE_EMSG_TRACK, Boolean.class);
+ // We do not check PARAMETER_EXPOSE_CAPTION_FORMATS here, and we do it in setParameters
+ // instead. Checking that the value is a List is insufficient to catch wrong parameter
+ // value types.
EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName);
}
}
diff --git a/apex/media/framework/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java
index 081e76ab0215e9373f900ce5036bbbd743c31d56..6560afedab0fa584733ed25a147f21ad9681729c 100644
--- a/apex/media/framework/java/android/media/MediaSession2.java
+++ b/apex/media/framework/java/android/media/MediaSession2.java
@@ -404,7 +404,7 @@ public class MediaSession2 implements AutoCloseable {
mCallback.onPostConnect(MediaSession2.this, controllerInfo);
connected = true;
} finally {
- if (!connected) {
+ if (!connected || isClosed()) {
if (DEBUG) {
Log.d(TAG, "Rejecting connection or notifying that session is closed"
+ ", controllerInfo=" + controllerInfo);
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 732caecbd4a771f396be8a9d6e0e537b18d38b26..be553feb1d34bf6b924b7c503e95b7258211fcac 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -38,11 +38,6 @@ java_sdk_library {
":framework-permission-sources",
],
- // TODO(b/155480189) - Remove naming_scheme once references have been resolved.
- // Temporary java_sdk_library component naming scheme to use to ease the transition from separate
- // modules to java_sdk_library.
- naming_scheme: "framework-modules",
-
apex_available: [
"com.android.permission",
"test_com.android.permission",
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index 61449763540b7039471155d40152cb9a44f5184f..7f3187949712f160f04d5a4ef286581135ceaf8b 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -20,14 +20,26 @@ filegroup {
path: "java",
}
-java_library {
+java_sdk_library {
name: "service-permission",
+ defaults: ["framework-system-server-module-defaults"],
+ visibility: [
+ "//frameworks/base/services/core",
+ "//frameworks/base/apex/permission",
+ "//frameworks/base/apex/permission/testing",
+ "//frameworks/base/apex/permission/tests",
+ "//frameworks/base/services/tests/mockingservicestests",
+ ],
+ impl_library_visibility: [
+ "//visibility:override",
+ "//frameworks/base/apex/permission/tests",
+ "//frameworks/base/services/tests/mockingservicestests",
+ "//frameworks/base/services/tests/servicestests",
+ ],
srcs: [
":service-permission-sources",
],
- sdk_version: "module_current",
libs: [
- "framework-annotations-lib",
"framework-permission",
],
apex_available: [
@@ -36,28 +48,3 @@ java_library {
],
installable: true,
}
-
-droidstubs {
- name: "service-permission-stubs-srcs",
- srcs: [ ":service-permission-sources" ],
- defaults: ["service-module-stubs-srcs-defaults"],
- check_api: {
- last_released: {
- api_file: ":service-permission.api.system-server.latest",
- removed_api_file: ":service-permission-removed.api.system-server.latest",
- },
- api_lint: {
- new_since: ":service-permission.api.system-server.latest",
- },
- },
- visibility: ["//visibility:private"],
- dist: { dest: "service-permission.txt" },
-}
-
-java_library {
- name: "service-permission-stubs",
- srcs: [":service-permission-stubs-srcs"],
- defaults: ["service-module-stubs-defaults"],
- visibility: ["//frameworks/base/services/core"],
- dist: { dest: "service-permission.jar" },
-}
diff --git a/apex/permission/service/api/current.txt b/apex/permission/service/api/current.txt
index c76cc3275737eb90679575c386f887226b2c65b7..d802177e249b3f97128699222e65c35e57ba7540 100644
--- a/apex/permission/service/api/current.txt
+++ b/apex/permission/service/api/current.txt
@@ -1,46 +1 @@
// Signature format: 2.0
-package com.android.permission.persistence {
-
- public interface RuntimePermissionsPersistence {
- method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance();
- method public void deleteForUser(@NonNull android.os.UserHandle);
- method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle);
- method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
- }
-
- public final class RuntimePermissionsState {
- ctor public RuntimePermissionsState(int, @Nullable String, @NonNull java.util.Map>, @NonNull java.util.Map>);
- method @Nullable public String getFingerprint();
- method @NonNull public java.util.Map> getPackagePermissions();
- method @NonNull public java.util.Map> getSharedUserPermissions();
- method public int getVersion();
- field public static final int NO_VERSION = -1; // 0xffffffff
- }
-
- public static final class RuntimePermissionsState.PermissionState {
- ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int);
- method public int getFlags();
- method @NonNull public String getName();
- method public boolean isGranted();
- }
-
-}
-
-package com.android.role.persistence {
-
- public interface RolesPersistence {
- method @NonNull public static com.android.role.persistence.RolesPersistence createInstance();
- method public void deleteForUser(@NonNull android.os.UserHandle);
- method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle);
- method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
- }
-
- public final class RolesState {
- ctor public RolesState(int, @Nullable String, @NonNull java.util.Map>);
- method @Nullable public String getPackagesHash();
- method @NonNull public java.util.Map> getRoles();
- method public int getVersion();
- }
-
-}
-
diff --git a/apex/permission/service/api/system-server-current.txt b/apex/permission/service/api/system-server-current.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c76cc3275737eb90679575c386f887226b2c65b7
--- /dev/null
+++ b/apex/permission/service/api/system-server-current.txt
@@ -0,0 +1,46 @@
+// Signature format: 2.0
+package com.android.permission.persistence {
+
+ public interface RuntimePermissionsPersistence {
+ method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance();
+ method public void deleteForUser(@NonNull android.os.UserHandle);
+ method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle);
+ method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
+ }
+
+ public final class RuntimePermissionsState {
+ ctor public RuntimePermissionsState(int, @Nullable String, @NonNull java.util.Map>, @NonNull java.util.Map>);
+ method @Nullable public String getFingerprint();
+ method @NonNull public java.util.Map> getPackagePermissions();
+ method @NonNull public java.util.Map> getSharedUserPermissions();
+ method public int getVersion();
+ field public static final int NO_VERSION = -1; // 0xffffffff
+ }
+
+ public static final class RuntimePermissionsState.PermissionState {
+ ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int);
+ method public int getFlags();
+ method @NonNull public String getName();
+ method public boolean isGranted();
+ }
+
+}
+
+package com.android.role.persistence {
+
+ public interface RolesPersistence {
+ method @NonNull public static com.android.role.persistence.RolesPersistence createInstance();
+ method public void deleteForUser(@NonNull android.os.UserHandle);
+ method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle);
+ method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
+ }
+
+ public final class RolesState {
+ ctor public RolesState(int, @Nullable String, @NonNull java.util.Map>);
+ method @Nullable public String getPackagesHash();
+ method @NonNull public java.util.Map> getRoles();
+ method public int getVersion();
+ }
+
+}
+
diff --git a/apex/permission/service/api/system-server-removed.txt b/apex/permission/service/api/system-server-removed.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d802177e249b3f97128699222e65c35e57ba7540
--- /dev/null
+++ b/apex/permission/service/api/system-server-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java
index 0ae44603516e0e4022c815c0f5b5fe747ec20727..569a78c0ab410906a27f6467910e119ff15a1d4c 100644
--- a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java
+++ b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java
@@ -20,6 +20,8 @@ import android.annotation.NonNull;
/**
* Utility class for IO.
+ *
+ * @hide
*/
public class IoUtils {
diff --git a/apex/permission/tests/Android.bp b/apex/permission/tests/Android.bp
index a1f7a544434c60733d6cb01728fa13ab0e94d9c7..271e328c11394cc31874f164ffbc80384f13dfec 100644
--- a/apex/permission/tests/Android.bp
+++ b/apex/permission/tests/Android.bp
@@ -19,7 +19,7 @@ android_test {
"java/**/*.kt",
],
static_libs: [
- "service-permission",
+ "service-permission.impl",
"androidx.test.rules",
"androidx.test.ext.junit",
"androidx.test.ext.truth",
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 78496c4074f840e2fd3769e9017fa77a547d04cd..d19faa97e223e6019a178228a5aa51404dd8494d 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -20,8 +20,8 @@ genrule {
name: "statslog-statsd-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module statsd" +
- " --javaPackage com.android.internal.util --javaClass StatsdStatsLog",
- out: ["com/android/internal/util/StatsdStatsLog.java"],
+ " --javaPackage com.android.internal.statsd --javaClass StatsdStatsLog",
+ out: ["com/android/internal/statsd/StatsdStatsLog.java"],
}
java_library_static {
@@ -51,11 +51,6 @@ java_sdk_library {
defaults: ["framework-module-defaults"],
installable: true,
- // TODO(b/155480189) - Remove naming_scheme once references have been resolved.
- // Temporary java_sdk_library component naming scheme to use to ease the transition from separate
- // modules to java_sdk_library.
- naming_scheme: "framework-modules",
-
srcs: [
":framework-statsd-sources",
],
@@ -65,7 +60,7 @@ java_sdk_library {
"android.os",
"android.util",
// From :statslog-statsd-java-gen
- "com.android.internal.util",
+ "com.android.internal.statsd",
],
api_packages: [
@@ -79,6 +74,7 @@ java_sdk_library {
visibility: [
"//frameworks/base", // Framework
"//frameworks/base/apex/statsd:__subpackages__", // statsd apex
+ "//frameworks/base/packages/Tethering", // Tethering
"//frameworks/opt/net/wifi/service", // wifi service
"//packages/providers/MediaProvider", // MediaProvider apk
],
@@ -94,26 +90,3 @@ java_sdk_library {
"test_com.android.os.statsd",
],
}
-
-android_test {
- name: "FrameworkStatsdTest",
- platform_apis: true,
- srcs: [
- // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
- ":framework-statsd-sources",
- "test/**/*.java",
- ],
- manifest: "test/AndroidManifest.xml",
- static_libs: [
- "androidx.test.rules",
- "truth-prebuilt",
- ],
- libs: [
- "android.test.runner.stubs",
- "android.test.base.stubs",
- ],
- test_suites: [
- "device-tests",
- ],
-}
-
diff --git a/apex/statsd/framework/java/android/util/StatsEvent.java b/apex/statsd/framework/java/android/util/StatsEvent.java
index 8bd36a516b1272c74e62c07a39bf217b2544ad94..8be5c63f31e343504758173da181baf61486cac5 100644
--- a/apex/statsd/framework/java/android/util/StatsEvent.java
+++ b/apex/statsd/framework/java/android/util/StatsEvent.java
@@ -26,6 +26,8 @@ import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.Arrays;
+
/**
* StatsEvent builds and stores the buffer sent over the statsd socket.
* This class defines and encapsulates the socket protocol.
@@ -224,7 +226,9 @@ public final class StatsEvent {
// Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag.
// See android_util_StatsLog.cpp.
- private static final int MAX_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
+ private static final int MAX_PUSH_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
+
+ private static final int MAX_PULL_PAYLOAD_SIZE = 50 * 1024; // 50 KB
private final int mAtomId;
private final byte[] mPayload;
@@ -619,6 +623,7 @@ public final class StatsEvent {
@NonNull
public Builder usePooledBuffer() {
mUsePooledBuffer = true;
+ mBuffer.setMaxSize(MAX_PUSH_PAYLOAD_SIZE, mPos);
return this;
}
@@ -694,8 +699,9 @@ public final class StatsEvent {
@GuardedBy("sLock")
private static Buffer sPool;
- private final byte[] mBytes = new byte[MAX_PAYLOAD_SIZE];
+ private byte[] mBytes = new byte[MAX_PUSH_PAYLOAD_SIZE];
private boolean mOverflow = false;
+ private int mMaxSize = MAX_PULL_PAYLOAD_SIZE;
@NonNull
private static Buffer obtain() {
@@ -717,15 +723,26 @@ public final class StatsEvent {
}
private void release() {
- synchronized (sLock) {
- if (null == sPool) {
- sPool = this;
+ // Recycle this Buffer if its size is MAX_PUSH_PAYLOAD_SIZE or under.
+ if (mBytes.length <= MAX_PUSH_PAYLOAD_SIZE) {
+ synchronized (sLock) {
+ if (null == sPool) {
+ sPool = this;
+ }
}
}
}
private void reset() {
mOverflow = false;
+ mMaxSize = MAX_PULL_PAYLOAD_SIZE;
+ }
+
+ private void setMaxSize(final int maxSize, final int numBytesWritten) {
+ mMaxSize = maxSize;
+ if (numBytesWritten > maxSize) {
+ mOverflow = true;
+ }
}
private boolean hasOverflowed() {
@@ -740,11 +757,28 @@ public final class StatsEvent {
* @return true if space is available, false otherwise.
**/
private boolean hasEnoughSpace(final int index, final int numBytes) {
- final boolean result = index + numBytes < MAX_PAYLOAD_SIZE;
- if (!result) {
+ final int totalBytesNeeded = index + numBytes;
+
+ if (totalBytesNeeded > mMaxSize) {
mOverflow = true;
+ return false;
}
- return result;
+
+ // Expand buffer if needed.
+ if (mBytes.length < mMaxSize && totalBytesNeeded > mBytes.length) {
+ int newSize = mBytes.length;
+ do {
+ newSize *= 2;
+ } while (newSize <= totalBytesNeeded);
+
+ if (newSize > mMaxSize) {
+ newSize = mMaxSize;
+ }
+
+ mBytes = Arrays.copyOf(mBytes, newSize);
+ }
+
+ return true;
}
/**
diff --git a/apex/statsd/framework/java/android/util/StatsLog.java b/apex/statsd/framework/java/android/util/StatsLog.java
index 4eeae57fe19540e9147396eaa73f40abb91bf6b2..0a9f4ebabdf078191eabf7f0ac828f1976b84ae7 100644
--- a/apex/statsd/framework/java/android/util/StatsLog.java
+++ b/apex/statsd/framework/java/android/util/StatsLog.java
@@ -28,7 +28,7 @@ import android.os.IStatsd;
import android.os.Process;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.util.StatsdStatsLog;
+import com.android.internal.statsd.StatsdStatsLog;
/**
* StatsLog provides an API for developers to send events to statsd. The events can be used to
diff --git a/apex/statsd/framework/test/Android.bp b/apex/statsd/framework/test/Android.bp
new file mode 100644
index 0000000000000000000000000000000000000000..b113d595b57c1d4a2bd654acdb064b549f40613a
--- /dev/null
+++ b/apex/statsd/framework/test/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "FrameworkStatsdTest",
+ platform_apis: true,
+ srcs: [
+ // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
+ ":framework-statsd-sources",
+ "**/*.java",
+ ],
+ manifest: "AndroidManifest.xml",
+ static_libs: [
+ "androidx.test.rules",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.runner.stubs",
+ "android.test.base.stubs",
+ ],
+ test_suites: [
+ "device-tests",
+ "mts",
+ ],
+}
\ No newline at end of file
diff --git a/apex/statsd/framework/test/AndroidTest.xml b/apex/statsd/framework/test/AndroidTest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fb519150ecd56fc8578981f2dac81aad36391394
--- /dev/null
+++ b/apex/statsd/framework/test/AndroidTest.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apex/statsd/framework/test/src/android/util/StatsEventTest.java b/apex/statsd/framework/test/src/android/util/StatsEventTest.java
index 7b511553a26f24d40e37329f775dea679cbc2f0e..8d263699d9c8fed099c2960098b39bd73994cefa 100644
--- a/apex/statsd/framework/test/src/android/util/StatsEventTest.java
+++ b/apex/statsd/framework/test/src/android/util/StatsEventTest.java
@@ -33,6 +33,7 @@ import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.Random;
/**
* Internal tests for {@link StatsEvent}.
@@ -644,6 +645,165 @@ public class StatsEventTest {
statsEvent.release();
}
+ @Test
+ public void testLargePulledEvent() {
+ final int expectedAtomId = 10_020;
+ byte[] field1 = new byte[10 * 1024];
+ new Random().nextBytes(field1);
+
+ final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+ final StatsEvent statsEvent =
+ StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build();
+ final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+ assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+ final ByteBuffer buffer =
+ ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+ assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_OBJECT);
+
+ assertWithMessage("Incorrect number of elements in root object")
+ .that(buffer.get())
+ .isEqualTo(3);
+
+ assertWithMessage("First element is not timestamp")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_LONG);
+
+ assertWithMessage("Incorrect timestamp")
+ .that(buffer.getLong())
+ .isIn(Range.closed(minTimestamp, maxTimestamp));
+
+ assertWithMessage("Second element is not atom id")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_INT);
+
+ assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
+
+ assertWithMessage("Third element is not byte array")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_BYTE_ARRAY);
+
+ final byte[] field1Actual = getByteArrayFromByteBuffer(buffer);
+ assertWithMessage("Incorrect field 1").that(field1Actual).isEqualTo(field1);
+
+ assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+ statsEvent.release();
+ }
+
+ @Test
+ public void testPulledEventOverflow() {
+ final int expectedAtomId = 10_020;
+ byte[] field1 = new byte[50 * 1024];
+ new Random().nextBytes(field1);
+
+ final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+ final StatsEvent statsEvent =
+ StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build();
+ final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+ assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+ final ByteBuffer buffer =
+ ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+ assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_OBJECT);
+
+ assertWithMessage("Incorrect number of elements in root object")
+ .that(buffer.get())
+ .isEqualTo(3);
+
+ assertWithMessage("First element is not timestamp")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_LONG);
+
+ assertWithMessage("Incorrect timestamp")
+ .that(buffer.getLong())
+ .isIn(Range.closed(minTimestamp, maxTimestamp));
+
+ assertWithMessage("Second element is not atom id")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_INT);
+
+ assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
+
+ assertWithMessage("Third element is not errors type")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_ERRORS);
+
+ final int errorMask = buffer.getInt();
+
+ assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask")
+ .that(errorMask)
+ .isEqualTo(StatsEvent.ERROR_OVERFLOW);
+
+ assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+ statsEvent.release();
+ }
+
+ @Test
+ public void testPushedEventOverflow() {
+ final int expectedAtomId = 10_020;
+ byte[] field1 = new byte[10 * 1024];
+ new Random().nextBytes(field1);
+
+ final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+ final StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(expectedAtomId)
+ .writeByteArray(field1)
+ .usePooledBuffer()
+ .build();
+ final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+ assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+ final ByteBuffer buffer =
+ ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+ assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_OBJECT);
+
+ assertWithMessage("Incorrect number of elements in root object")
+ .that(buffer.get())
+ .isEqualTo(3);
+
+ assertWithMessage("First element is not timestamp")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_LONG);
+
+ assertWithMessage("Incorrect timestamp")
+ .that(buffer.getLong())
+ .isIn(Range.closed(minTimestamp, maxTimestamp));
+
+ assertWithMessage("Second element is not atom id")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_INT);
+
+ assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
+
+ assertWithMessage("Third element is not errors type")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_ERRORS);
+
+ final int errorMask = buffer.getInt();
+
+ assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask")
+ .that(errorMask)
+ .isEqualTo(StatsEvent.ERROR_OVERFLOW);
+
+ assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+ statsEvent.release();
+ }
+
private static byte[] getByteArrayFromByteBuffer(final ByteBuffer buffer) {
final int numBytes = buffer.getInt();
byte[] bytes = new byte[numBytes];
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
index 240222ea9411ca0d13cac3d592f02f84bb12e690..6108a324e15e3bf4351e5ea7a3b9130207197e64 100644
--- a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
@@ -33,7 +33,6 @@ import com.android.internal.os.StatsdConfigProto.PullAtomPackages;
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import com.android.internal.os.statsd.StatsConfigUtils;
import com.android.internal.os.statsd.protos.TestAtoms;
import com.android.os.AtomsProto.Atom;
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/StatsConfigUtils.java
similarity index 99%
rename from apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java
rename to apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/StatsConfigUtils.java
index d0d140092586512db421341b40cdbd8fd1a0b4e8..b5afb94886de20b20a9fdb89a6c3e8bea25bfca9 100644
--- a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/StatsConfigUtils.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.internal.os.statsd;
+package com.android.internal.os.statsd.libstats;
import static com.google.common.truth.Truth.assertThat;
diff --git a/api/test-current.txt b/api/test-current.txt
index 6f3c9ee2df37ec4544d3988c7f9ecdb8a2a4c254..3838bad57aa71afde90d774b7ef096559e999ca3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -32,6 +32,7 @@ package android {
}
public static final class R.bool {
+ field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
}
@@ -193,6 +194,7 @@ package android.app {
method public static int opToDefaultMode(@NonNull String);
method public static String opToPermission(int);
method public static int permissionToOpCode(String);
+ method @RequiresPermission("android.permission.MANAGE_APPOPS") public void rebootHistory(long);
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 setHistoryParameters(int, long, int);
@@ -1298,6 +1300,130 @@ package android.hardware.display {
}
+package android.hardware.hdmi {
+
+ public final class HdmiControlManager {
+ method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
+ method @RequiresPermission("android.permission.HDMI_CEC") public void setStandbyMode(boolean);
+ field public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
+ field public static final int AVR_VOLUME_MUTED = 101; // 0x65
+ field public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 162; // 0xa2
+ field public static final int CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION = 160; // 0xa0
+ field public static final int CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE = 161; // 0xa1
+ field public static final int CLEAR_TIMER_STATUS_TIMER_CLEARED = 128; // 0x80
+ field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_INFO_AVAILABLE = 2; // 0x2
+ field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_MATCHING = 1; // 0x1
+ field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_RECORDING = 0; // 0x0
+ field public static final int CONTROL_STATE_CHANGED_REASON_SETTING = 1; // 0x1
+ field public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3; // 0x3
+ field public static final int CONTROL_STATE_CHANGED_REASON_START = 0; // 0x0
+ field public static final int CONTROL_STATE_CHANGED_REASON_WAKEUP = 2; // 0x2
+ field public static final int DEVICE_EVENT_ADD_DEVICE = 1; // 0x1
+ field public static final int DEVICE_EVENT_REMOVE_DEVICE = 2; // 0x2
+ field public static final int DEVICE_EVENT_UPDATE_DEVICE = 3; // 0x3
+ field public static final String EXTRA_MESSAGE_EXTRA_PARAM1 = "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1";
+ field public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID";
+ field public static final int ONE_TOUCH_RECORD_ALREADY_RECORDING = 18; // 0x12
+ field public static final int ONE_TOUCH_RECORD_CEC_DISABLED = 51; // 0x33
+ field public static final int ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION = 49; // 0x31
+ field public static final int ONE_TOUCH_RECORD_DISALLOW_TO_COPY = 13; // 0xd
+ field public static final int ONE_TOUCH_RECORD_DISALLOW_TO_FUTHER_COPIES = 14; // 0xe
+ field public static final int ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN = 50; // 0x32
+ field public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 10; // 0xa
+ field public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PLUG_NUMBER = 9; // 0x9
+ field public static final int ONE_TOUCH_RECORD_MEDIA_PROBLEM = 21; // 0x15
+ field public static final int ONE_TOUCH_RECORD_MEDIA_PROTECTED = 19; // 0x13
+ field public static final int ONE_TOUCH_RECORD_NOT_ENOUGH_SPACE = 22; // 0x16
+ field public static final int ONE_TOUCH_RECORD_NO_MEDIA = 16; // 0x10
+ field public static final int ONE_TOUCH_RECORD_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 12; // 0xc
+ field public static final int ONE_TOUCH_RECORD_NO_SOURCE_SIGNAL = 20; // 0x14
+ field public static final int ONE_TOUCH_RECORD_OTHER_REASON = 31; // 0x1f
+ field public static final int ONE_TOUCH_RECORD_PARENT_LOCK_ON = 23; // 0x17
+ field public static final int ONE_TOUCH_RECORD_PLAYING = 17; // 0x11
+ field public static final int ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS = 48; // 0x30
+ field public static final int ONE_TOUCH_RECORD_RECORDING_ALREADY_TERMINATED = 27; // 0x1b
+ field public static final int ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE = 3; // 0x3
+ field public static final int ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE = 1; // 0x1
+ field public static final int ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE = 2; // 0x2
+ field public static final int ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT = 4; // 0x4
+ field public static final int ONE_TOUCH_RECORD_RECORDING_TERMINATED_NORMALLY = 26; // 0x1a
+ field public static final int ONE_TOUCH_RECORD_UNABLE_ANALOGUE_SERVICE = 6; // 0x6
+ field public static final int ONE_TOUCH_RECORD_UNABLE_DIGITAL_SERVICE = 5; // 0x5
+ field public static final int ONE_TOUCH_RECORD_UNABLE_SELECTED_SERVICE = 7; // 0x7
+ field public static final int ONE_TOUCH_RECORD_UNSUPPORTED_CA = 11; // 0xb
+ field public static final int OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT = 1; // 0x1
+ field public static final int OSD_MESSAGE_AVR_VOLUME_CHANGED = 2; // 0x2
+ field public static final int POWER_STATUS_ON = 0; // 0x0
+ field public static final int POWER_STATUS_STANDBY = 1; // 0x1
+ field public static final int POWER_STATUS_TRANSIENT_TO_ON = 2; // 0x2
+ field public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3; // 0x3
+ field public static final int POWER_STATUS_UNKNOWN = -1; // 0xffffffff
+ field @Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4; // 0x4
+ field public static final int RESULT_COMMUNICATION_FAILED = 7; // 0x7
+ field public static final int RESULT_EXCEPTION = 5; // 0x5
+ field public static final int RESULT_INCORRECT_MODE = 6; // 0x6
+ field public static final int RESULT_SOURCE_NOT_AVAILABLE = 2; // 0x2
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ field public static final int RESULT_TARGET_NOT_AVAILABLE = 3; // 0x3
+ field public static final int RESULT_TIMEOUT = 1; // 0x1
+ field public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 3; // 0x3
+ field public static final int TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION = 1; // 0x1
+ field public static final int TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE = 2; // 0x2
+ field public static final int TIMER_RECORDING_RESULT_EXTRA_NO_ERROR = 0; // 0x0
+ field public static final int TIMER_RECORDING_TYPE_ANALOGUE = 2; // 0x2
+ field public static final int TIMER_RECORDING_TYPE_DIGITAL = 1; // 0x1
+ field public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3; // 0x3
+ field public static final int TIMER_STATUS_MEDIA_INFO_NOT_PRESENT = 2; // 0x2
+ field public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_NOT_PROTECTED = 0; // 0x0
+ field public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_PROTECTED = 1; // 0x1
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_CA_NOT_SUPPORTED = 6; // 0x6
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_CLOCK_FAILURE = 10; // 0xa
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_DATE_OUT_OF_RANGE = 2; // 0x2
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_DUPLICATED = 14; // 0xe
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PHYSICAL_NUMBER = 5; // 0x5
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PLUG_NUMBER = 4; // 0x4
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_SEQUENCE = 3; // 0x3
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_CA_ENTITLEMENTS = 7; // 0x7
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_FREE_TIME = 1; // 0x1
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_PARENTAL_LOCK_ON = 9; // 0x9
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_UNSUPPORTED_RESOLUTION = 8; // 0x8
+ field public static final int TIMER_STATUS_PROGRAMMED_INFO_ENOUGH_SPACE = 8; // 0x8
+ field public static final int TIMER_STATUS_PROGRAMMED_INFO_MIGHT_NOT_ENOUGH_SPACE = 11; // 0xb
+ field public static final int TIMER_STATUS_PROGRAMMED_INFO_NOT_ENOUGH_SPACE = 9; // 0x9
+ field public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 10; // 0xa
+ }
+
+ public final class HdmiControlServiceWrapper {
+ ctor public HdmiControlServiceWrapper();
+ method @NonNull public android.hardware.hdmi.HdmiControlManager createHdmiControlManager();
+ method @BinderThread public void setDeviceTypes(@NonNull int[]);
+ method @BinderThread public void setPortInfo(@NonNull java.util.List);
+ field public static final int DEVICE_PURE_CEC_SWITCH = 6; // 0x6
+ }
+
+ public final class HdmiPortInfo implements android.os.Parcelable {
+ ctor public HdmiPortInfo(int, int, int, boolean, boolean, boolean);
+ method public int describeContents();
+ method public int getAddress();
+ method public int getId();
+ method public int getType();
+ method public boolean isArcSupported();
+ method public boolean isCecSupported();
+ method public boolean isMhlSupported();
+ field @NonNull public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int PORT_INPUT = 0; // 0x0
+ field public static final int PORT_OUTPUT = 1; // 0x1
+ }
+
+ public class HdmiSwitchClient {
+ method public int getDeviceType();
+ method @NonNull public java.util.List getPortInfo();
+ method public void sendKeyEvent(int, boolean);
+ method public void sendVendorCommand(int, byte[], boolean);
+ }
+
+}
+
package android.hardware.lights {
public final class Light implements android.os.Parcelable {
@@ -3198,6 +3324,7 @@ package android.provider {
field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
field public static final String NOTIFICATION_BADGING = "notification_badging";
field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content";
+ field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
@@ -4977,6 +5104,7 @@ package android.view {
method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
method public int getType();
method public boolean hasAccess(int);
+ field public static final int FLAG_TRUSTED = 128; // 0x80
field public static final int TYPE_EXTERNAL = 2; // 0x2
field public static final int TYPE_INTERNAL = 1; // 0x1
field public static final int TYPE_OVERLAY = 4; // 0x4
@@ -5021,7 +5149,7 @@ package android.view {
}
public final class SurfaceControl implements android.os.Parcelable {
- ctor public SurfaceControl(@NonNull android.view.SurfaceControl);
+ ctor public SurfaceControl(@NonNull android.view.SurfaceControl, @NonNull String);
method public static long acquireFrameRateFlexibilityToken();
method public boolean isSameSurface(@NonNull android.view.SurfaceControl);
method public static void releaseFrameRateFlexibilityToken(long);
@@ -5378,6 +5506,19 @@ package android.widget {
}
+package android.widget.inline {
+
+ public class InlineContentView extends android.view.ViewGroup {
+ method public void setChildSurfacePackageUpdater(@Nullable android.widget.inline.InlineContentView.SurfacePackageUpdater);
+ }
+
+ public static interface InlineContentView.SurfacePackageUpdater {
+ method public void getSurfacePackage(@NonNull java.util.function.Consumer);
+ method public void onSurfacePackageReleased();
+ }
+
+}
+
package android.window {
public final class DisplayAreaInfo implements android.os.Parcelable {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 2adbc1f6e1aed1912296f4b804cba9fa184d2de7..7c30c8b1e1dd1a2f9b5d4fa8e044b4c3ad746959 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -17,8 +17,8 @@
package com.android.commands.am;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
-import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
import android.app.IActivityManager;
import android.app.IInstrumentationWatcher;
@@ -512,7 +512,7 @@ public class Instrument {
flags |= INSTR_FLAG_DISABLE_TEST_API_CHECKS;
}
if (disableIsolatedStorage) {
- flags |= INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
+ flags |= INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
}
if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
abi)) {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 3bcabe56f89e7f14ea107c3c36036a8bc7bb5c55..bb2de17b42f37bb09972608295e362a19832814b 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -349,6 +349,25 @@ EGLConfig BootAnimation::getEglConfig(const EGLDisplay& display) {
return config;
}
+ui::Size BootAnimation::limitSurfaceSize(int width, int height) const {
+ ui::Size limited(width, height);
+ bool wasLimited = false;
+ const float aspectRatio = float(width) / float(height);
+ if (mMaxWidth != 0 && width > mMaxWidth) {
+ limited.height = mMaxWidth / aspectRatio;
+ limited.width = mMaxWidth;
+ wasLimited = true;
+ }
+ if (mMaxHeight != 0 && limited.height > mMaxHeight) {
+ limited.height = mMaxHeight;
+ limited.width = mMaxHeight * aspectRatio;
+ wasLimited = true;
+ }
+ SLOGV_IF(wasLimited, "Surface size has been limited to [%dx%d] from [%dx%d]",
+ limited.width, limited.height, width, height);
+ return limited;
+}
+
status_t BootAnimation::readyToRun() {
mAssets.addDefaultAssets();
@@ -362,8 +381,10 @@ status_t BootAnimation::readyToRun() {
if (error != NO_ERROR)
return error;
- const ui::Size& resolution = displayConfig.resolution;
-
+ mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0);
+ mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0);
+ ui::Size resolution = displayConfig.resolution;
+ resolution = limitSurfaceSize(resolution.width, resolution.height);
// create the native surface
sp control = session()->createSurface(String8("BootAnimation"),
resolution.getWidth(), resolution.getHeight(), PIXEL_FORMAT_RGB_565);
@@ -459,8 +480,9 @@ void BootAnimation::resizeSurface(int newWidth, int newHeight) {
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(mDisplay, mSurface);
- mWidth = newWidth;
- mHeight = newHeight;
+ const auto limitedSize = limitSurfaceSize(newWidth, newHeight);
+ mWidth = limitedSize.width;
+ mHeight = limitedSize.height;
SurfaceComposerClient::Transaction t;
t.setSize(mFlingerSurfaceControl, mWidth, mHeight);
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 36cd91bdee0d969fb0e923a3cbb30540a81cb147..6ba7fd450fbb2a0328071cafad8ae924fd99068c 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -170,6 +170,7 @@ private:
bool findBootAnimationFileInternal(const std::vector& files);
bool preloadAnimation();
EGLConfig getEglConfig(const EGLDisplay&);
+ ui::Size limitSurfaceSize(int width, int height) const;
void resizeSurface(int newWidth, int newHeight);
void checkExit();
@@ -181,6 +182,8 @@ private:
Texture mAndroid[2];
int mWidth;
int mHeight;
+ int mMaxWidth = 0;
+ int mMaxHeight = 0;
int mCurrentInset;
int mTargetInset;
bool mUseNpotTextures = false;
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index ef5c4cec9166a06ae49a457f864d05f1daa2ae87..878cef94b6743ce8ba91ab92f590c7ed0d7fd89a 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -23,9 +23,16 @@ cc_defaults {
"misc-*",
"readability-*",
],
+ tidy_checks_as_errors: [
+ "modernize-*",
+ "-modernize-avoid-c-arrays",
+ "-modernize-use-trailing-return-type",
+ "android-*",
+ "misc-*",
+ "readability-*",
+ ],
tidy_flags: [
"-system-headers",
- "-warnings-as-errors=*",
],
}
@@ -168,13 +175,13 @@ cc_binary {
],
host_supported: true,
srcs: [
+ "idmap2/CommandUtils.cpp",
"idmap2/Create.cpp",
"idmap2/CreateMultiple.cpp",
"idmap2/Dump.cpp",
"idmap2/Lookup.cpp",
"idmap2/Main.cpp",
"idmap2/Scan.cpp",
- "idmap2/Verify.cpp",
],
target: {
android: {
diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/CommandUtils.cpp
similarity index 70%
rename from cmds/idmap2/idmap2/Verify.cpp
rename to cmds/idmap2/idmap2/CommandUtils.cpp
index 9cb67b33e6cf0529b59e55dfa29f4640df157d27..8f5845bf2e53e312f616ce3ee40742d379a3d5dd 100644
--- a/cmds/idmap2/idmap2/Verify.cpp
+++ b/cmds/idmap2/idmap2/CommandUtils.cpp
@@ -19,30 +19,19 @@
#include
#include
-#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::IdmapHeader;
using android::idmap2::Result;
using android::idmap2::Unit;
-Result Verify(const std::vector& args) {
- SYSTRACE << "Verify " << args;
- std::string idmap_path;
-
- const CommandLineOptions opts =
- CommandLineOptions("idmap2 verify")
- .MandatoryOption("--idmap-path", "input: path to idmap file to verify", &idmap_path);
-
- const auto opts_ok = opts.Parse(args);
- if (!opts_ok) {
- return opts_ok.GetError();
- }
-
+Result Verify(const std::string& idmap_path, const std::string& target_path,
+ const std::string& overlay_path, PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) {
+ SYSTRACE << "Verify " << idmap_path;
std::ifstream fin(idmap_path);
const std::unique_ptr header = IdmapHeader::FromBinaryStream(fin);
fin.close();
@@ -50,7 +39,8 @@ Result Verify(const std::vector& args) {
return Error("failed to parse idmap header");
}
- const auto header_ok = header->IsUpToDate();
+ const auto header_ok = header->IsUpToDate(target_path.c_str(), overlay_path.c_str(),
+ fulfilled_policies, enforce_overlayable);
if (!header_ok) {
return Error(header_ok.GetError(), "idmap not up to date");
}
diff --git a/cmds/idmap2/idmap2/CommandUtils.h b/cmds/idmap2/idmap2/CommandUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..e717e046d15d56dd1e26389199c06db2a88db147
--- /dev/null
+++ b/cmds/idmap2/idmap2/CommandUtils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_IDMAP2_COMMAND_UTILS_H_
+#define IDMAP2_IDMAP2_COMMAND_UTILS_H_
+
+#include "idmap2/PolicyUtils.h"
+#include "idmap2/Result.h"
+
+android::idmap2::Result Verify(const std::string& idmap_path,
+ const std::string& target_path,
+ const std::string& overlay_path,
+ PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable);
+
+#endif // IDMAP2_IDMAP2_COMMAND_UTILS_H_
diff --git a/cmds/idmap2/idmap2/Commands.h b/cmds/idmap2/idmap2/Commands.h
index e626738a28956b2734bd7b6e5bd680182b1be5b5..69eea8d262d27e1d0032ab7988eed643fa9583fd 100644
--- a/cmds/idmap2/idmap2/Commands.h
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -27,6 +27,5 @@ android::idmap2::Result CreateMultiple(const std::vector<
android::idmap2::Result Dump(const std::vector& args);
android::idmap2::Result Lookup(const std::vector& args);
android::idmap2::Result Scan(const std::vector& args);
-android::idmap2::Result Verify(const std::vector& args);
#endif // IDMAP2_IDMAP2_COMMANDS_H_
diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp
index 4b70acc2969c4e28ae89ac1df3391af46f2c7cb8..abdfaf4dccabfdefd15dcad16514416f7d74b38c 100644
--- a/cmds/idmap2/idmap2/CreateMultiple.cpp
+++ b/cmds/idmap2/idmap2/CreateMultiple.cpp
@@ -26,6 +26,7 @@
#include "android-base/stringprintf.h"
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/CommandLineOptions.h"
+#include "idmap2/CommandUtils.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
#include "idmap2/Policies.h"
@@ -103,7 +104,8 @@ Result CreateMultiple(const std::vector& args) {
continue;
}
- if (!Verify(std::vector({"--idmap-path", idmap_path}))) {
+ if (!Verify(idmap_path, target_apk_path, overlay_apk_path, fulfilled_policies,
+ !ignore_overlayable)) {
const std::unique_ptr overlay_apk = ApkAssets::Load(overlay_apk_path);
if (!overlay_apk) {
LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index a07e793d9f473c0d0c73b770e03ffd8841eb3819..fb093f0f22a42f4c0a665bd9aa31702c88356048 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -53,9 +53,8 @@ void PrintUsage(const NameToFunctionMap& commands, std::ostream& out) {
int main(int argc, char** argv) {
SYSTRACE << "main";
const NameToFunctionMap commands = {
- {"create", Create}, {"create-multiple", CreateMultiple},
- {"dump", Dump}, {"lookup", Lookup},
- {"scan", Scan}, {"verify", Verify},
+ {"create", Create}, {"create-multiple", CreateMultiple}, {"dump", Dump}, {"lookup", Lookup},
+ {"scan", Scan},
};
if (argc <= 1) {
PrintUsage(commands, std::cerr);
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index da0453216f0321d266019df20c14d9e0e8f381b3..36250450cc74ec1052e38c6e45a292efa507257a 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -27,8 +27,11 @@
#include "Commands.h"
#include "android-base/properties.h"
#include "idmap2/CommandLineOptions.h"
+#include "idmap2/CommandUtils.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
+#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
@@ -48,6 +51,7 @@ using android::idmap2::policy::kPolicyVendor;
using android::idmap2::utils::ExtractOverlayManifestInfo;
using android::idmap2::utils::FindFiles;
using android::idmap2::utils::OverlayManifestInfo;
+using android::idmap2::utils::PoliciesToBitmaskResult;
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
@@ -215,7 +219,15 @@ Result Scan(const std::vector& args) {
std::stringstream stream;
for (const auto& overlay : interesting_apks) {
- if (!Verify(std::vector({"--idmap-path", overlay.idmap_path}))) {
+ const auto policy_bitmask = PoliciesToBitmaskResult(overlay.policies);
+ if (!policy_bitmask) {
+ LOG(WARNING) << "failed to create idmap for overlay apk path \"" << overlay.apk_path
+ << "\": " << policy_bitmask.GetErrorMessage();
+ continue;
+ }
+
+ if (!Verify(overlay.idmap_path, target_apk_path, overlay.apk_path, *policy_bitmask,
+ !overlay.ignore_overlayable)) {
std::vector create_args = {"--target-apk-path", target_apk_path,
"--overlay-apk-path", overlay.apk_path,
"--idmap-path", overlay.idmap_path};
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index a93184ff4787593402e11978b6bda374d869c8bd..f95b73f17222ff6cc3a1fc90ffe4ebd36762ee82 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -33,16 +33,19 @@
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
#include "idmap2/ZipFile.h"
#include "utils/String8.h"
using android::IPCThreadState;
+using android::base::StringPrintf;
using android::binder::Status;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::GetPackageCrc;
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
+using android::idmap2::ZipFile;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
using android::idmap2::utils::UidHasWriteAccessToPath;
@@ -66,6 +69,21 @@ PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) {
return static_cast(arg);
}
+Status GetCrc(const std::string& apk_path, uint32_t* out_crc) {
+ const auto zip = ZipFile::Open(apk_path);
+ if (!zip) {
+ return error(StringPrintf("failed to open apk %s", apk_path.c_str()));
+ }
+
+ const auto crc = GetPackageCrc(*zip);
+ if (!crc) {
+ return error(crc.GetErrorMessage());
+ }
+
+ *out_crc = *crc;
+ return ok();
+}
+
} // namespace
namespace android::os {
@@ -98,12 +116,12 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
}
Status Idmap2Service::verifyIdmap(const std::string& target_apk_path,
- const std::string& overlay_apk_path,
- int32_t fulfilled_policies ATTRIBUTE_UNUSED,
- bool enforce_overlayable ATTRIBUTE_UNUSED,
- int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
+ const std::string& overlay_apk_path, int32_t fulfilled_policies,
+ bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
+ bool* _aidl_return) {
SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_apk_path;
assert(_aidl_return);
+
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
std::ifstream fin(idmap_path);
const std::unique_ptr header = IdmapHeader::FromBinaryStream(fin);
@@ -113,35 +131,36 @@ Status Idmap2Service::verifyIdmap(const std::string& target_apk_path,
return error("failed to parse idmap header");
}
- if (strcmp(header->GetTargetPath().data(), target_apk_path.data()) != 0) {
- *_aidl_return = false;
- return ok();
- }
-
- if (target_apk_path != kFrameworkPath) {
- *_aidl_return = (bool) header->IsUpToDate();
+ uint32_t target_crc;
+ if (target_apk_path == kFrameworkPath && android_crc_) {
+ target_crc = *android_crc_;
} else {
- if (!android_crc_) {
- // Loading the framework zip can take several milliseconds. Cache the crc of the framework
- // resource APK to reduce repeated work during boot.
- const auto target_zip = idmap2::ZipFile::Open(target_apk_path);
- if (!target_zip) {
- return error(base::StringPrintf("failed to open target %s", target_apk_path.c_str()));
- }
-
- const auto target_crc = GetPackageCrc(*target_zip);
- if (!target_crc) {
- return error(target_crc.GetErrorMessage());
- }
-
- android_crc_ = *target_crc;
+ auto target_crc_status = GetCrc(target_apk_path, &target_crc);
+ if (!target_crc_status.isOk()) {
+ *_aidl_return = false;
+ return target_crc_status;
+ }
+
+ // Loading the framework zip can take several milliseconds. Cache the crc of the framework
+ // resource APK to reduce repeated work during boot.
+ if (target_apk_path == kFrameworkPath) {
+ android_crc_ = target_crc;
}
+ }
- *_aidl_return = (bool) header->IsUpToDate(android_crc_.value());
+ uint32_t overlay_crc;
+ auto overlay_crc_status = GetCrc(overlay_apk_path, &overlay_crc);
+ if (!overlay_crc_status.isOk()) {
+ *_aidl_return = false;
+ return overlay_crc_status;
}
- // TODO(b/119328308): Check that the set of fulfilled policies of the overlay has not changed
- return ok();
+ auto up_to_date =
+ header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), target_crc, overlay_crc,
+ ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable);
+
+ *_aidl_return = static_cast(up_to_date);
+ return *_aidl_return ? ok() : error(up_to_date.GetErrorMessage());
}
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 77a7b30a230e0616d304406d544eead0773f6bef..0f05592b70f3c695610eb6fc43da119420588eb9 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -117,6 +117,14 @@ class IdmapHeader {
return overlay_crc_;
}
+ inline uint32_t GetFulfilledPolicies() const {
+ return fulfilled_policies_;
+ }
+
+ bool GetEnforceOverlayable() const {
+ return enforce_overlayable_;
+ }
+
inline StringPiece GetTargetPath() const {
return StringPiece(target_path_);
}
@@ -132,8 +140,11 @@ class IdmapHeader {
// Invariant: anytime the idmap data encoding is changed, the idmap version
// field *must* be incremented. Because of this, we know that if the idmap
// header is up-to-date the entire file is up-to-date.
- Result IsUpToDate() const;
- Result IsUpToDate(uint32_t target_crc_) const;
+ Result IsUpToDate(const char* target_path, const char* overlay_path,
+ PolicyBitmask fulfilled_policies, bool enforce_overlayable) const;
+ Result IsUpToDate(const char* target_path, const char* overlay_path, uint32_t target_crc,
+ uint32_t overlay_crc, PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) const;
void accept(Visitor* v) const;
@@ -145,6 +156,8 @@ class IdmapHeader {
uint32_t version_;
uint32_t target_crc_;
uint32_t overlay_crc_;
+ uint32_t fulfilled_policies_;
+ bool enforce_overlayable_;
char target_path_[kIdmapStringLength];
char overlay_path_[kIdmapStringLength];
std::string debug_info_;
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index 362dcb36007a409aff1a0f898c3e869766f5505e..255212ad4c66b5e32f3b4e7be71c77476d52f822 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -66,6 +66,8 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) {
Write32(header.GetVersion());
Write32(header.GetTargetCrc());
Write32(header.GetOverlayCrc());
+ Write32(header.GetFulfilledPolicies());
+ Write8(static_cast(header.GetEnforceOverlayable()));
WriteString256(header.GetTargetPath());
WriteString256(header.GetOverlayPath());
WriteString(header.GetDebugInfo());
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 706b842b3b4782c96b27cb40264cd1cae37668fd..23c25a7089decbf7b99d34929bcd13cd8da263aa 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -112,14 +112,17 @@ Result GetPackageCrc(const ZipFile& zip) {
std::unique_ptr IdmapHeader::FromBinaryStream(std::istream& stream) {
std::unique_ptr idmap_header(new IdmapHeader());
-
+ uint8_t enforce_overlayable;
if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) ||
!Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) ||
+ !Read32(stream, &idmap_header->fulfilled_policies_) || !Read8(stream, &enforce_overlayable) ||
!ReadString256(stream, idmap_header->target_path_) ||
!ReadString256(stream, idmap_header->overlay_path_)) {
return nullptr;
}
+ idmap_header->enforce_overlayable_ = static_cast(enforce_overlayable);
+
auto debug_str = ReadString(stream);
if (!debug_str) {
return nullptr;
@@ -129,21 +132,37 @@ std::unique_ptr IdmapHeader::FromBinaryStream(std::istream& s
return std::move(idmap_header);
}
-Result IdmapHeader::IsUpToDate() const {
- const std::unique_ptr target_zip = ZipFile::Open(target_path_);
+Result IdmapHeader::IsUpToDate(const char* target_path, const char* overlay_path,
+ PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) const {
+ const std::unique_ptr target_zip = ZipFile::Open(target_path);
if (!target_zip) {
- return Error("failed to open target %s", GetTargetPath().to_string().c_str());
+ return Error("failed to open target %s", target_path);
}
- Result target_crc = GetPackageCrc(*target_zip);
+ const Result target_crc = GetPackageCrc(*target_zip);
if (!target_crc) {
return Error("failed to get target crc");
}
- return IsUpToDate(*target_crc);
+ const std::unique_ptr overlay_zip = ZipFile::Open(overlay_path);
+ if (!overlay_zip) {
+ return Error("failed to overlay target %s", overlay_path);
+ }
+
+ const Result overlay_crc = GetPackageCrc(*overlay_zip);
+ if (!overlay_crc) {
+ return Error("failed to get overlay crc");
+ }
+
+ return IsUpToDate(target_path, overlay_path, *target_crc, *overlay_crc, fulfilled_policies,
+ enforce_overlayable);
}
-Result IdmapHeader::IsUpToDate(uint32_t target_crc) const {
+Result IdmapHeader::IsUpToDate(const char* target_path, const char* overlay_path,
+ uint32_t target_crc, uint32_t overlay_crc,
+ PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) const {
if (magic_ != kIdmapMagic) {
return Error("bad magic: actual 0x%08x, expected 0x%08x", magic_, kIdmapMagic);
}
@@ -157,19 +176,29 @@ Result IdmapHeader::IsUpToDate(uint32_t target_crc) const {
target_crc);
}
- const std::unique_ptr overlay_zip = ZipFile::Open(overlay_path_);
- if (!overlay_zip) {
- return Error("failed to open overlay %s", GetOverlayPath().to_string().c_str());
+ if (overlay_crc_ != overlay_crc) {
+ return Error("bad overlay crc: idmap version 0x%08x, file system version 0x%08x", overlay_crc_,
+ overlay_crc);
}
- Result overlay_crc = GetPackageCrc(*overlay_zip);
- if (!overlay_crc) {
- return Error("failed to get overlay crc");
+ if (fulfilled_policies_ != fulfilled_policies) {
+ return Error("bad fulfilled policies: idmap version 0x%08x, file system version 0x%08x",
+ fulfilled_policies, fulfilled_policies_);
}
- if (overlay_crc_ != *overlay_crc) {
- return Error("bad overlay crc: idmap version 0x%08x, file system version 0x%08x", overlay_crc_,
- *overlay_crc);
+ if (enforce_overlayable != enforce_overlayable_) {
+ return Error("bad enforce overlayable: idmap version %s, file system version %s",
+ enforce_overlayable ? "true" : "false", enforce_overlayable_ ? "true" : "false");
+ }
+
+ if (strcmp(target_path, target_path_) != 0) {
+ return Error("bad target path: idmap version %s, file system version %s", target_path,
+ target_path_);
+ }
+
+ if (strcmp(overlay_path, overlay_path_) != 0) {
+ return Error("bad overlay path: idmap version %s, file system version %s", overlay_path,
+ overlay_path_);
}
return Unit{};
@@ -320,6 +349,9 @@ Result> Idmap::FromApkAssets(const ApkAssets& targe
}
header->overlay_crc_ = *crc;
+ header->fulfilled_policies_ = fulfilled_policies;
+ header->enforce_overlayable_ = enforce_overlayable;
+
if (target_apk_path.size() > sizeof(header->target_path_)) {
return Error("target apk path \"%s\" longer than maximum size %zu", target_apk_path.c_str(),
sizeof(header->target_path_));
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 751c60c4add4cc84edf2254659b5301b47c62b96..3f62a2ae20294bf10de2f30b0fbe584f0d6fe5e5 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -23,10 +23,12 @@
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "androidfw/ApkAssets.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
using android::ApkAssets;
+using android::idmap2::policy::PoliciesToDebugString;
namespace {
@@ -39,9 +41,6 @@ size_t StringSizeWhenEncoded(const std::string& s) {
namespace android::idmap2 {
-// verbatim copy fomr PrettyPrintVisitor.cpp, move to common utils
-#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
-
void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
}
@@ -50,6 +49,9 @@ void RawPrintVisitor::visit(const IdmapHeader& header) {
print(header.GetVersion(), "version");
print(header.GetTargetCrc(), "target crc");
print(header.GetOverlayCrc(), "overlay crc");
+ print(header.GetFulfilledPolicies(), "fulfilled policies: %s",
+ PoliciesToDebugString(header.GetFulfilledPolicies()).c_str());
+ print(static_cast(header.GetEnforceOverlayable()), "enforce overlayable");
print(header.GetTargetPath().to_string(), kIdmapStringLength, "target path");
print(header.GetOverlayPath().to_string(), kIdmapStringLength, "overlay path");
print("...", StringSizeWhenEncoded(header.GetDebugInfo()), "debug info");
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 44acbcaf8ace236b88845e363af2555329f3534b..34589a1c39dcce3669047647a3f702fd2f2288fe 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -61,8 +61,7 @@ Result CheckOverlayable(const LoadedPackage& target_package,
const ResourceId& target_resource) {
static constexpr const PolicyBitmask sDefaultPolicies =
PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
- PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE |
- PolicyFlags::ACTOR_SIGNATURE;
+ PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE;
// If the resource does not have an overlayable definition, allow the resource to be overlaid if
// the overlay is preinstalled or signed with the same signature as the target.
@@ -292,13 +291,6 @@ Result ResourceMapping::FromApkAssets(const ApkAssets& target_a
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable,
LogInfo& log_info) {
- if (enforce_overlayable) {
- log_info.Info(LogMessage() << "fulfilled_policies="
- << ConcatPolicies(BitmaskToPolicies(fulfilled_policies))
- << " enforce_overlayable="
- << (enforce_overlayable ? "true" : "false"));
- }
-
AssetManager2 target_asset_manager;
if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true /* invalidate_caches */,
false /* filter_incompatible_configs*/)) {
diff --git a/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
index 4973b7638d10b71f2c4aa593cf66de2d17f97e31..5bd353af4ad3b71af5aab51af6150215dc8570fd 100644
--- a/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
+++ b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
@@ -21,9 +21,12 @@
#include
#include
+#include "android-base/stringprintf.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
+using android::base::StringPrintf;
+
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
@@ -48,6 +51,29 @@ inline static const std::array, 8> kPolicySt
{kPolicySystem, PolicyFlags::SYSTEM_PARTITION},
{kPolicyVendor, PolicyFlags::VENDOR_PARTITION},
};
+
+inline static std::string PoliciesToDebugString(PolicyBitmask policies) {
+ std::string str;
+ uint32_t remaining = policies;
+ for (auto const& policy : kPolicyStringToFlag) {
+ if ((policies & policy.second) != policy.second) {
+ continue;
+ }
+ if (!str.empty()) {
+ str.append("|");
+ }
+ str.append(policy.first.data());
+ remaining &= ~policy.second;
+ }
+ if (remaining != 0) {
+ if (!str.empty()) {
+ str.append("|");
+ }
+ str.append(StringPrintf("0x%08x", remaining));
+ }
+ return !str.empty() ? str : "none";
+}
+
} // namespace android::idmap2::policy
#endif // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index db4778c8ee095eae1b1079dd025471742c2a37a2..5fea7bcdaac59c304cb4ef83b740e07ebdcee6c9 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -48,6 +48,11 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
ASSERT_TRUE(result2);
const auto idmap2 = std::move(*result2);
+ ASSERT_EQ(idmap1->GetHeader()->GetFulfilledPolicies(),
+ idmap2->GetHeader()->GetFulfilledPolicies());
+ ASSERT_EQ(idmap1->GetHeader()->GetEnforceOverlayable(),
+ idmap2->GetHeader()->GetEnforceOverlayable());
+ ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath());
ASSERT_EQ(idmap1->GetHeader()->GetTargetCrc(), idmap2->GetHeader()->GetTargetCrc());
ASSERT_EQ(idmap1->GetHeader()->GetTargetPath(), idmap2->GetHeader()->GetTargetPath());
ASSERT_EQ(idmap1->GetData().size(), 1U);
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 87da36c01192a33d19b59ce52a1f4ca419ee6352..6fab5e0f8ae121a457e7ffeabd922fe7582e0f5a 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -62,9 +62,11 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
std::unique_ptr header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
- ASSERT_EQ(header->GetVersion(), 0x03U);
+ ASSERT_EQ(header->GetVersion(), 0x04U);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
+ ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
+ ASSERT_EQ(header->GetEnforceOverlayable(), true);
ASSERT_EQ(header->GetTargetPath().to_string(), "targetX.apk");
ASSERT_EQ(header->GetOverlayPath().to_string(), "overlayX.apk");
ASSERT_EQ(header->GetDebugInfo(), "debug");
@@ -73,7 +75,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
std::string raw(reinterpret_cast(idmap_raw_data), idmap_raw_data_len);
// overwrite the target path string, including the terminating null, with '.'
- for (size_t i = 0x10; i < 0x110; i++) {
+ for (size_t i = 0x15; i < 0x115; i++) {
raw[i] = '.';
}
std::istringstream stream(raw);
@@ -82,7 +84,7 @@ TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
}
TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
- const size_t offset = 0x21c;
+ const size_t offset = 0x221;
std::string raw(reinterpret_cast(idmap_raw_data + offset),
idmap_raw_data_len - offset);
std::istringstream stream(raw);
@@ -94,7 +96,7 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
}
TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
- const size_t offset = 0x21c;
+ const size_t offset = 0x221;
std::string raw(reinterpret_cast(idmap_raw_data + offset),
idmap_raw_data_len - offset);
std::istringstream stream(raw);
@@ -128,9 +130,11 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x03U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
+ ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), 0x11);
+ ASSERT_EQ(idmap->GetHeader()->GetEnforceOverlayable(), true);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "targetX.apk");
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlayX.apk");
@@ -180,9 +184,11 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x03U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
+ ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
+ ASSERT_EQ(idmap->GetHeader()->GetEnforceOverlayable(), true);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
}
@@ -389,7 +395,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
std::unique_ptr header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
- ASSERT_TRUE(header->IsUpToDate());
+ ASSERT_TRUE(header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
// magic: bytes (0x0, 0x03)
std::string bad_magic_string(stream.str());
@@ -402,7 +409,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
IdmapHeader::FromBinaryStream(bad_magic_stream);
ASSERT_THAT(bad_magic_header, NotNull());
ASSERT_NE(header->GetMagic(), bad_magic_header->GetMagic());
- ASSERT_FALSE(bad_magic_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
// version: bytes (0x4, 0x07)
std::string bad_version_string(stream.str());
@@ -415,7 +423,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
IdmapHeader::FromBinaryStream(bad_version_stream);
ASSERT_THAT(bad_version_header, NotNull());
ASSERT_NE(header->GetVersion(), bad_version_header->GetVersion());
- ASSERT_FALSE(bad_version_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
// target crc: bytes (0x8, 0xb)
std::string bad_target_crc_string(stream.str());
@@ -428,7 +437,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
IdmapHeader::FromBinaryStream(bad_target_crc_stream);
ASSERT_THAT(bad_target_crc_header, NotNull());
ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc());
- ASSERT_FALSE(bad_target_crc_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
// overlay crc: bytes (0xc, 0xf)
std::string bad_overlay_crc_string(stream.str());
@@ -441,27 +451,55 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
IdmapHeader::FromBinaryStream(bad_overlay_crc_stream);
ASSERT_THAT(bad_overlay_crc_header, NotNull());
ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc());
- ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate());
-
- // target path: bytes (0x10, 0x10f)
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+
+ // fulfilled policy: bytes (0x10, 0x13)
+ std::string bad_policy_string(stream.str());
+ bad_policy_string[0x10] = '.';
+ bad_policy_string[0x11] = '.';
+ bad_policy_string[0x12] = '.';
+ bad_policy_string[0x13] = '.';
+ std::stringstream bad_policy_stream(bad_policy_string);
+ std::unique_ptr bad_policy_header =
+ IdmapHeader::FromBinaryStream(bad_policy_stream);
+ ASSERT_THAT(bad_policy_header, NotNull());
+ ASSERT_NE(header->GetFulfilledPolicies(), bad_policy_header->GetFulfilledPolicies());
+ ASSERT_FALSE(bad_policy_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+
+ // enforce overlayable: bytes (0x14)
+ std::string bad_enforce_string(stream.str());
+ bad_enforce_string[0x14] = '\0';
+ std::stringstream bad_enforce_stream(bad_enforce_string);
+ std::unique_ptr bad_enforce_header =
+ IdmapHeader::FromBinaryStream(bad_enforce_stream);
+ ASSERT_THAT(bad_enforce_header, NotNull());
+ ASSERT_NE(header->GetEnforceOverlayable(), bad_enforce_header->GetEnforceOverlayable());
+ ASSERT_FALSE(bad_enforce_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
+
+ // target path: bytes (0x15, 0x114)
std::string bad_target_path_string(stream.str());
- bad_target_path_string[0x10] = '\0';
+ bad_target_path_string[0x15] = '\0';
std::stringstream bad_target_path_stream(bad_target_path_string);
std::unique_ptr bad_target_path_header =
IdmapHeader::FromBinaryStream(bad_target_path_stream);
ASSERT_THAT(bad_target_path_header, NotNull());
ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath());
- ASSERT_FALSE(bad_target_path_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
- // overlay path: bytes (0x110, 0x20f)
+ // overlay path: bytes (0x115, 0x214)
std::string bad_overlay_path_string(stream.str());
- bad_overlay_path_string[0x110] = '\0';
+ bad_overlay_path_string[0x115] = '\0';
std::stringstream bad_overlay_path_stream(bad_overlay_path_string);
std::unique_ptr bad_overlay_path_header =
IdmapHeader::FromBinaryStream(bad_overlay_path_stream);
ASSERT_THAT(bad_overlay_path_header, NotNull());
ASSERT_NE(header->GetOverlayPath(), bad_overlay_path_header->GetOverlayPath());
- ASSERT_FALSE(bad_overlay_path_header->IsUpToDate());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
}
class TestVisitor : public Visitor {
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 5c5c81edee90d500739524bebedd6815a22327b3..b268d5add14130ce1771febe0077f9637f44d660 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -43,6 +43,8 @@ namespace android::idmap2 {
<< str << "--------"; \
} while (0)
+#define ADDRESS "[0-9a-f]{8}: "
+
TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
fclose(stderr); // silence expected warnings
@@ -62,15 +64,16 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
RawPrintVisitor visitor(stream);
(*idmap)->accept(&visitor);
-#define ADDRESS "[0-9a-f]{8}: "
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000003 version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 version\n", stream.str());
ASSERT_CONTAINS_REGEX(
StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
stream.str());
ASSERT_CONTAINS_REGEX(
StringPrintf(ADDRESS "%s overlay crc\n", android::idmap2::TestConstants::OVERLAY_CRC_STRING),
stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 01 enforce overlayable\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count\n", stream.str());
@@ -83,7 +86,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 value: integer/int1\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str());
-#undef ADDRESS
}
TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
@@ -99,22 +101,23 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
RawPrintVisitor visitor(stream);
(*idmap)->accept(&visitor);
- ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000004: 00000003 version\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000008: 00001234 target crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000000c: 00005678 overlay crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000021c: 7f target package id\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000021d: 7f overlay package id\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000021e: 00000003 target entry count\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000222: 00000003 overlay entry count\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000226: 00000000 string pool index offset\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000022a: 00000000 string pool byte length\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000022e: 7f020000 target id\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000232: 01 type: reference\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000233: 7f020000 value\n"), std::string::npos);
-
- ASSERT_NE(stream.str().find("00000249: 7f020000 overlay id\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000024d: 7f020000 target id\n"), std::string::npos);
+ ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 01 enforce overlayable\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool index offset\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool byte length\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 01 type: reference\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 value\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str());
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 5754eaf078a98fc7cb5dd9bf6bb509aa8d91e011..de039f440e33f1d73c1ed3e1125dbd8bad3309e1 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -287,26 +287,66 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTarget
R::overlay::string::str4, false /* rewrite */));
}
-
-// Overlays that are pre-installed or are signed with the same signature as the target/actor can
+// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
// overlay packages that have not defined overlayable resources.
-TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
- constexpr PolicyBitmask kDefaultPolicies =
- PolicyFlags::SIGNATURE | PolicyFlags::ACTOR_SIGNATURE | PolicyFlags::PRODUCT_PARTITION |
- PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION | PolicyFlags::ODM_PARTITION |
- PolicyFlags::OEM_PARTITION;
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
+ auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
+ "/overlay/overlay-no-name.apk", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
+}
- for (PolicyBitmask policy = 1U << (sizeof(PolicyBitmask) * 8 - 1); policy > 0;
- policy = policy >> 1U) {
+// Overlays that are pre-installed or are signed with the same signature as the target can overlay
+// packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
+ auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
"/system-overlay-invalid/system-overlay-invalid.apk",
- policy, /* enforce_overlayable */ true);
- ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ fulfilled_policies,
+ /* enforce_overlayable */ true);
- const size_t expected_overlaid = (policy & kDefaultPolicies) != 0 ? 10U : 0U;
- ASSERT_EQ(expected_overlaid, resources->GetTargetToOverlayMap().size())
- << "Incorrect number of resources overlaid through policy " << policy;
- }
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 10U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::not_overlayable,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::other, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_actor,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_odm,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_oem,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_product,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_signature,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(
+ res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+ };
+
+ CheckEntries(PolicyFlags::SIGNATURE);
+ CheckEntries(PolicyFlags::PRODUCT_PARTITION);
+ CheckEntries(PolicyFlags::SYSTEM_PARTITION);
+ CheckEntries(PolicyFlags::VENDOR_PARTITION);
+ CheckEntries(PolicyFlags::ODM_PARTITION);
+ CheckEntries(PolicyFlags::OEM_PARTITION);
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index e899589c7e61035116f6b4435b4a0f4953c5cffe..b599dcb0069ab33e986db07966df63f4b4dcbf56 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -30,7 +30,7 @@ const unsigned char idmap_raw_data[] = {
0x49, 0x44, 0x4d, 0x50,
// 0x4: version
- 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00,
// 0x8: target crc
0x34, 0x12, 0x00, 0x00,
@@ -38,7 +38,13 @@ const unsigned char idmap_raw_data[] = {
// 0xc: overlay crc
0x78, 0x56, 0x00, 0x00,
- // 0x10: target path "targetX.apk"
+ // 0x10: fulfilled policies
+ 0x11, 0x00, 0x00, 0x00,
+
+ // 0x14: enforce overlayable
+ 0x01,
+
+ // 0x15: target path "targetX.apk"
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -56,7 +62,7 @@ const unsigned char idmap_raw_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- // 0x110: overlay path "overlayX.apk"
+ // 0x115: overlay path "overlayX.apk"
0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -74,7 +80,7 @@ const unsigned char idmap_raw_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- // 0x210: debug string
+ // 0x215: debug string
// string length, including terminating null
0x08, 0x00, 0x00, 0x00,
@@ -82,63 +88,63 @@ const unsigned char idmap_raw_data[] = {
0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
// DATA HEADER
- // 0x21c: target_package_id
+ // 0x221: target_package_id
0x7f,
- // 0x21d: overlay_package_id
+ // 0x222: overlay_package_id
0x7f,
- // 0x21e: target_entry_count
+ // 0x223: target_entry_count
0x03, 0x00, 0x00, 0x00,
- // 0x222: overlay_entry_count
+ // 0x227: overlay_entry_count
0x03, 0x00, 0x00, 0x00,
- // 0x226: string_pool_offset
+ // 0x22b: string_pool_offset
0x00, 0x00, 0x00, 0x00,
- // 0x22a: string_pool_byte_length
+ // 0x22f: string_pool_byte_length
0x00, 0x00, 0x00, 0x00,
// TARGET ENTRIES
- // 0x22e: 0x7f020000
+ // 0x233: 0x7f020000
0x00, 0x00, 0x02, 0x7f,
- // 0x232: TYPE_REFERENCE
+ // 0x237: TYPE_REFERENCE
0x01,
- // 0x233: 0x7f020000
+ // 0x238: 0x7f020000
0x00, 0x00, 0x02, 0x7f,
- // 0x237: 0x7f030000
+ // 0x23c: 0x7f030000
0x00, 0x00, 0x03, 0x7f,
- // 0x23b: TYPE_REFERENCE
+ // 0x240: TYPE_REFERENCE
0x01,
- // 0x23c: 0x7f030000
+ // 0x241: 0x7f030000
0x00, 0x00, 0x03, 0x7f,
- // 0x240: 0x7f030002
+ // 0x245: 0x7f030002
0x02, 0x00, 0x03, 0x7f,
- // 0x244: TYPE_REFERENCE
+ // 0x249: TYPE_REFERENCE
0x01,
- // 0x245: 0x7f030001
+ // 0x24a: 0x7f030001
0x01, 0x00, 0x03, 0x7f,
// OVERLAY ENTRIES
- // 0x249: 0x7f020000 -> 0x7f020000
+ // 0x24e: 0x7f020000 -> 0x7f020000
0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
- // 0x251: 0x7f030000 -> 0x7f030000
+ // 0x256: 0x7f030000 -> 0x7f030000
0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
- // 0x259: 0x7f030001 -> 0x7f030002
+ // 0x25e: 0x7f030001 -> 0x7f030002
0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f};
-const unsigned int idmap_raw_data_len = 0x261;
+const unsigned int idmap_raw_data_len = 0x266;
std::string GetTestDataPath();
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 61e5eb07130c3ae2213ce4a658ac7c51fce60e6b..33e764988b7516461fa5fb476e627d616db5fc3f 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -477,14 +477,15 @@ status_t TextDumpsysSection::Execute(ReportWriter* writer) const {
// Run dumping thread
const uint64_t start = Nanotime();
- std::thread worker([&]() {
+ std::thread worker([write_fd = std::move(dumpPipe.writeFd()), service = std::move(service),
+ this]() mutable {
// Don't crash the service if writing to a closed pipe (may happen if dumping times out)
signal(SIGPIPE, sigpipe_handler);
- status_t err = service->dump(dumpPipe.writeFd().get(), mArgs);
+ status_t err = service->dump(write_fd.get(), this->mArgs);
if (err != OK) {
ALOGW("[%s] dump thread failed. Error: %s", this->name.string(), strerror(-err));
}
- dumpPipe.writeFd().reset();
+ write_fd.reset();
});
// Collect dump content
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 3dbe4139502458b45e472efdf6b50aa101902a72..0617eb6c0e6633b75564e9e7c869591b36def9de 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -116,7 +116,6 @@ cc_defaults {
"libcutils",
"libgtest_prod",
"libprotoutil",
- "libstatsmetadata",
"libstatslog_statsd",
"libsysutils",
"libutils",
@@ -129,51 +128,6 @@ cc_defaults {
],
}
-// ================
-// libstatsmetadata
-// ================
-
-genrule {
- name: "atoms_info.h",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --atomsInfoHeader $(genDir)/atoms_info.h",
- out: [
- "atoms_info.h",
- ],
-}
-
-genrule {
- name: "atoms_info.cpp",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --atomsInfoCpp $(genDir)/atoms_info.cpp",
- out: [
- "atoms_info.cpp",
- ],
-}
-
-cc_library_static {
- name: "libstatsmetadata",
- host_supported: true,
- generated_sources: [
- "atoms_info.cpp",
- ],
- generated_headers: [
- "atoms_info.h",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- export_generated_headers: [
- "atoms_info.h",
- ],
- apex_available: [
- //TODO(b/149782403): Remove this once statsd no longer links against libstatsmetadata
- "com.android.os.statsd",
- "test_com.android.os.statsd",
- ],
-}
-
genrule {
name: "statslog_statsd.h",
tools: ["stats-log-api-gen"],
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 095dd1e9be5929df63f061f0163081226e778b1c..e7b32c56551a09b08ea8520ff67033e2ca557f7a 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -529,7 +529,9 @@ void StatsLogProcessor::OnConfigUpdatedLocked(
VLOG("StatsdConfig valid");
} else {
// If there is any error in the config, don't use it.
+ // Remove any existing config with the same key.
ALOGE("StatsdConfig NOT valid");
+ mMetricsManagers.erase(key);
}
}
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 7090bd46635dcbc217e17afd7d63761b341a4f74..23f2584655b0d3859f0d8111d97f783f785a1921 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -284,6 +284,7 @@ private:
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
+ FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 47bab2947aaf0638f2fa2e7dbf2e432f283e8c18..6f952f637506c903b4dff14e4d9b3b9e1998a9ee 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -267,8 +267,11 @@ void StatsService::dumpIncidentSection(int out) {
for (const ConfigKey& configKey : mConfigManager->GetAllConfigKeys()) {
uint64_t reportsListToken =
proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS_LIST);
+ // Don't include the current bucket to avoid skipping buckets.
+ // If we need to include the current bucket later, consider changing to NO_TIME_CONSTRAINTS
+ // or other alternatives to avoid skipping buckets for pulled metrics.
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
- true /* includeCurrentBucket */, false /* erase_data */,
+ false /* includeCurrentBucket */, false /* erase_data */,
ADB_DUMP,
FAST,
&proto);
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index 5722f923d11ec9c84abbd340ec871d821d2cfb13..6d9beb8f718de94d6cb3d5b8d0fb2367dc9d9704 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -60,11 +60,11 @@ void AlarmTracker::addSubscription(const Subscription& subscription) {
}
int64_t AlarmTracker::findNextAlarmSec(int64_t currentTimeSec) {
- if (currentTimeSec <= mAlarmSec) {
+ if (currentTimeSec < mAlarmSec) {
return mAlarmSec;
}
int64_t periodsForward =
- ((currentTimeSec - mAlarmSec) * MS_PER_SEC - 1) / mAlarmConfig.period_millis() + 1;
+ ((currentTimeSec - mAlarmSec) * MS_PER_SEC) / mAlarmConfig.period_millis() + 1;
return mAlarmSec + periodsForward * mAlarmConfig.period_millis() / MS_PER_SEC;
}
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 8527185d38910adab4a8f42f120fd32cd67cac0a..ff5717e4fa7868fb6a2858a1fc61f032e755f01e 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -110,8 +110,6 @@ extend google.protobuf.FieldOptions {
optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC];
- optional bool allow_from_any_uid = 50003 [default = false];
-
repeated string module = 50004;
optional bool truncate_timestamp = 50005 [default = false];
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 36a8b2cfc084149bfca3ce12d143102c4ac15f30..7a016522d597c9904c4211cb0a5660793959a78f 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -43,6 +43,7 @@ import "frameworks/base/core/proto/android/server/location/enums.proto";
import "frameworks/base/core/proto/android/service/procstats_enum.proto";
import "frameworks/base/core/proto/android/service/usb.proto";
import "frameworks/base/core/proto/android/stats/connectivity/network_stack.proto";
+import "frameworks/base/core/proto/android/stats/connectivity/tethering.proto";
import "frameworks/base/core/proto/android/stats/dnsresolver/dns_resolver.proto";
import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
@@ -144,8 +145,7 @@ message Atom {
PacketWakeupOccurred packet_wakeup_occurred = 44 [(module) = "framework"];
WallClockTimeShifted wall_clock_time_shifted = 45 [(module) = "framework"];
AnomalyDetected anomaly_detected = 46 [(module) = "statsd"];
- AppBreadcrumbReported app_breadcrumb_reported =
- 47 [(allow_from_any_uid) = true, (module) = "statsd"];
+ AppBreadcrumbReported app_breadcrumb_reported = 47 [(module) = "statsd"];
AppStartOccurred app_start_occurred = 48 [(module) = "framework", (module) = "statsdtest"];
AppStartCanceled app_start_canceled = 49 [(module) = "framework"];
AppStartFullyDrawn app_start_fully_drawn = 50 [(module) = "framework"];
@@ -156,7 +156,7 @@ message Atom {
AppStartMemoryStateCaptured app_start_memory_state_captured = 55 [(module) = "framework"];
ShutdownSequenceReported shutdown_sequence_reported = 56 [(module) = "framework"];
BootSequenceReported boot_sequence_reported = 57;
- DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true, (module) = "statsd"];
+ DaveyOccurred davey_occurred = 58 [(module) = "statsd"];
OverlayStateChanged overlay_state_changed =
59 [(module) = "framework", (module) = "statsdtest"];
ForegroundServiceStateChanged foreground_service_state_changed
@@ -185,15 +185,14 @@ message Atom {
WTFOccurred wtf_occurred = 80 [(module) = "framework"];
LowMemReported low_mem_reported = 81 [(module) = "framework"];
GenericAtom generic_atom = 82;
- KeyValuePairsAtom key_value_pairs_atom =
- 83 [(allow_from_any_uid) = true, (module) = "framework", (module) = "statsd"];
+ KeyValuePairsAtom key_value_pairs_atom = 83 [(module) = "framework", (module) = "statsd"];
VibratorStateChanged vibrator_state_changed = 84 [(module) = "framework"];
DeferredJobStatsReported deferred_job_stats_reported = 85 [(module) = "framework"];
ThermalThrottlingStateChanged thermal_throttling = 86 [deprecated=true];
BiometricAcquired biometric_acquired = 87 [(module) = "framework"];
BiometricAuthenticated biometric_authenticated = 88 [(module) = "framework"];
BiometricErrorOccurred biometric_error_occurred = 89 [(module) = "framework"];
- UiEventReported ui_event_reported = 90 [(module) = "framework"];
+ UiEventReported ui_event_reported = 90 [(module) = "framework", (module) = "sysui"];
BatteryHealthSnapshot battery_health_snapshot = 91;
SlowIo slow_io = 92;
BatteryCausedShutdown battery_caused_shutdown = 93;
@@ -316,7 +315,7 @@ message Atom {
AssistGestureFeedbackReported assist_gesture_feedback_reported = 175 [(module) = "sysui"];
AssistGestureProgressReported assist_gesture_progress_reported = 176 [(module) = "sysui"];
TouchGestureClassified touch_gesture_classified = 177 [(module) = "framework"];
- HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true, (module) = "framework"];
+ HiddenApiUsed hidden_api_used = 178 [(module) = "framework"];
StyleUIChanged style_ui_changed = 179 [(module) = "sysui"];
PrivacyIndicatorsInteracted privacy_indicators_interacted =
180 [(module) = "permissioncontroller"];
@@ -382,7 +381,7 @@ message Atom {
UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226;
CameraActionEvent camera_action_event = 227 [(module) = "framework"];
AppCompatibilityChangeReported app_compatibility_change_reported =
- 228 [(allow_from_any_uid) = true, (module) = "framework"];
+ 228 [(module) = "framework"];
PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"];
VmsClientConnectionStateChanged vms_client_connection_state_changed =
230 [(module) = "car"];
@@ -419,7 +418,7 @@ message Atom {
DisplayJankReported display_jank_reported = 257;
AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
SharesheetStarted sharesheet_started = 259 [(module) = "framework"];
- RankingSelected ranking_selected = 260 [(module) = "framework"];
+ RankingSelected ranking_selected = 260 [(module) = "framework", (module) = "sysui"];
TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"];
LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"];
PackageInstallerV2Reported package_installer_v2_reported = 263 [(module) = "framework"];
@@ -444,6 +443,48 @@ message Atom {
TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"];
MediaOutputOpSwitchReported mediaoutput_op_switch_reported =
277 [(module) = "settings"];
+ CellBroadcastMessageFiltered cb_message_filtered =
+ 278 [(module) = "cellbroadcast"];
+ TvTunerDvrStatus tv_tuner_dvr_status = 279 [(module) = "framework"];
+ TvCasSessionOpenStatus tv_cas_session_open_status =
+ 280 [(module) = "framework"];
+ AssistantInvocationReported assistant_invocation_reported = 281 [(module) = "framework"];
+ DisplayWakeReported display_wake_reported = 282 [(module) = "framework"];
+ CarUserHalModifyUserRequestReported car_user_hal_modify_user_request_reported =
+ 283 [(module) = "car"];
+ CarUserHalModifyUserResponseReported car_user_hal_modify_user_response_reported =
+ 284 [(module) = "car"];
+ CarUserHalPostSwitchResponseReported car_user_hal_post_switch_response_reported =
+ 285 [(module) = "car"];
+ CarUserHalInitialUserInfoRequestReported car_user_hal_initial_user_info_request_reported =
+ 286 [(module) = "car"];
+ CarUserHalInitialUserInfoResponseReported car_user_hal_initial_user_info_response_reported =
+ 287 [(module) = "car"];
+ CarUserHalUserAssociationRequestReported car_user_hal_user_association_request_reported =
+ 288 [(module) = "car"];
+ CarUserHalSetUserAssociationResponseReported car_user_hal_set_user_association_response_reported =
+ 289 [(module) = "car"];
+ NetworkIpProvisioningReported network_ip_provisioning_reported =
+ 290 [(module) = "network_stack"];
+ NetworkDhcpRenewReported network_dhcp_renew_reported = 291 [(module) = "network_stack"];
+ NetworkValidationReported network_validation_reported = 292 [(module) = "network_stack"];
+ NetworkStackQuirkReported network_stack_quirk_reported = 293 [(module) = "network_stack"];
+ MediametricsAudioRecordDeviceUsageReported mediametrics_audiorecorddeviceusage_reported =
+ 294;
+ MediametricsAudioThreadDeviceUsageReported mediametrics_audiothreaddeviceusage_reported =
+ 295;
+ MediametricsAudioTrackDeviceUsageReported mediametrics_audiotrackdeviceusage_reported =
+ 296;
+ MediametricsAudioDeviceConnectionReported mediametrics_audiodeviceconnection_reported =
+ 297;
+ BlobCommitted blob_committed = 298 [(module) = "framework"];
+ BlobLeased blob_leased = 299 [(module) = "framework"];
+ BlobOpened blob_opened = 300 [(module) = "framework"];
+ ContactsProviderStatusReported contacts_provider_status_reported = 301;
+ KeystoreKeyEventReported keystore_key_event_reported = 302;
+ NetworkTetheringReported network_tethering_reported =
+ 303 [(module) = "network_tethering"];
+ ImeTouchReported ime_touch_reported = 304 [(module) = "sysui"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -541,10 +582,13 @@ message Atom {
SimSlotState sim_slot_state = 10078 [(module) = "telephony"];
SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"];
SettingSnapshot setting_snapshot = 10080 [(module) = "framework"];
- DisplayWakeReason display_wake_reason = 10081 [(module) = "framework"];
+ BlobInfo blob_info = 10081 [(module) = "framework"];
DataUsageBytesTransfer data_usage_bytes_transfer = 10082 [(module) = "framework"];
BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered =
10083 [(module) = "framework"];
+ DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"];
+ GeneralExternalStorageAccessStats general_external_storage_access_stats =
+ 10085 [(module) = "mediaprovider"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3017,6 +3061,18 @@ message ExclusionRectStateChanged {
optional int32 duration_millis = 7;
}
+/**
+ * Logs when IME is on.
+ *
+ * Logged from: /packages/SystemUI/src/com/android/systemui/
+ statusbar/phone/NavigationBarView.java
+ *
+ */
+message ImeTouchReported {
+ optional int32 x_coordinate = 1; // X coordinate for ACTION_DOWN event.
+ optional int32 y_coordinate = 2; // Y coordinate for ACTION_DOWN event.
+}
+
/**
* Logs when Launcher (HomeScreen) UI has changed or was interacted.
*
@@ -4499,6 +4555,31 @@ message VmsClientConnectionStateChanged {
optional State state = 2;
}
+message MimeTypes {
+ repeated string mime_types = 1;
+}
+
+/**
+ * Logs statistics regarding accesses to external storage.
+ * All stats are normalized for one day period.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message GeneralExternalStorageAccessStats {
+ optional int32 uid = 1 [(is_uid) = true];
+ // Total number of accesses like creation, open, delete and rename/update.
+ // Includes file path and ContentResolver accesses
+ optional uint32 total_accesses = 2;
+ // Number of file path accesses, as opposed to file path and ContentResolver.
+ optional uint32 file_path_accesses = 3;
+ // Number of accesses on secondary volumes like SD cards.
+ // Includes file path and ContentResolver accesses
+ optional uint32 secondary_storage_accesses = 4;
+ // Comma-separated list of mime types that were accessed.
+ optional MimeTypes mime_types_accessed = 5;
+}
+
/**
* Logs when MediaProvider has successfully finished scanning a storage volume.
*
@@ -4870,6 +4951,98 @@ message SnapshotMergeReported {
optional int64 cow_file_size_bytes = 5;
}
+/**
+ * Event representing when BlobStoreManager.Session#commit() is called
+ *
+ * Logged from:
+ * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+ */
+message BlobCommitted {
+ // Uid of the Blob committer
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Id of the Blob committed
+ optional int64 blob_id = 2;
+
+ // Size of the Blob
+ optional int64 size = 3;
+
+ enum Result {
+ UNKNOWN = 0;
+ // Commit Succeeded
+ SUCCESS = 1;
+ // Commit Failed: Error occurred during commit
+ ERROR_DURING_COMMIT = 2;
+ // Commit Failed: Digest of the data did not match Blob digest
+ DIGEST_MISMATCH = 3;
+ // Commit Failed: Allowed count limit exceeded
+ COUNT_LIMIT_EXCEEDED = 4;
+ }
+ optional Result result = 4;
+}
+
+/**
+ * Event representing when BlobStoreManager#acquireLease() is called
+ *
+ * Logged from:
+ * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+ */
+message BlobLeased{
+ // Uid of the Blob leasee
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Id of the Blob leased or 0 if the Blob does not exist
+ optional int64 blob_id = 2;
+
+ // Size of the Blob or 0 if the Blob does not exist
+ optional int64 size = 3;
+
+ enum Result {
+ UNKNOWN = 0;
+ // Lease Succeeded
+ SUCCESS = 1;
+ // Lease Failed: Blob does not exist
+ BLOB_DNE = 2;
+ // Lease Failed: Leasee does not have access to the Blob
+ ACCESS_NOT_ALLOWED = 3;
+ // Lease Failed: Leasee requested an invalid expiry duration
+ LEASE_EXPIRY_INVALID = 4;
+ // Lease Failed: Leasee has exceeded the total data lease limit
+ DATA_SIZE_LIMIT_EXCEEDED = 5;
+ // Leasee Failed: Allowed count limit exceeded
+ COUNT_LIMIT_EXCEEDED = 6;
+ }
+ optional Result result = 4;
+}
+
+/**
+ * Event representing when BlobStoreManager#openBlob() is called
+ *
+ * Logged from:
+ * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+ */
+message BlobOpened{
+ // Uid of the Blob opener
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Id of the Blob opened or 0 if the Blob does not exist
+ optional int64 blob_id = 2;
+
+ // Size of the Blob or 0 if the Blob does not exist
+ optional int64 size = 3;
+
+ enum Result {
+ UNKNOWN = 0;
+ // Open Succeeded
+ SUCCESS = 1;
+ // Open Failed: Blob does not exist
+ BLOB_DNE = 2;
+ // Open Failed: Opener does not have access to the Blob
+ ACCESS_NOT_ALLOWED = 3;
+ }
+ optional Result result = 4;
+}
+
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
@@ -5798,7 +5971,7 @@ message ProcessStatsProto {
optional string process = 1;
// Uid of the process.
- optional int32 uid = 2;
+ optional int32 uid = 2 [(is_uid) = true];
// Information about how often kills occurred
message Kill {
@@ -5825,13 +5998,16 @@ message ProcessStatsProto {
repeated ProcessStatsAssociationProto assocs = 7;
}
-// Next Tag: 5
+// Next Tag: 6
message ProcessStatsAssociationProto {
// Procss Name of the associated process (client process of service binding)
optional string assoc_process_name = 1;
// Package Name of the associated package (client package of service binding)
- optional string assoc_package_name = 2;
+ optional string assoc_package_name = 2 [deprecated = true];
+
+ // UID of the associated process/package (client package of service binding)
+ optional int32 assoc_uid = 5 [(is_uid) = true];
// Total count of the times this association (service binding) appeared.
optional int32 total_count = 3;
@@ -5986,6 +6162,10 @@ message ProcessStatsAvailablePagesProto {
*/
message ProcStats {
optional ProcessStatsSectionProto proc_stats_section = 1;
+ // Data pulled from device into this is sometimes sharded across multiple atoms to work around
+ // a size limit. When this happens, this shard ID will contain an increasing 1-indexed integer
+ // with the number of this shard.
+ optional int32 shard_id = 2;
}
/**
@@ -6058,6 +6238,76 @@ message PackageNotificationChannelPreferences {
optional bool is_important_conversation = 10;
}
+/**
+ * Atom that represents an item in the list of Do Not Disturb rules, pulled from
+ * NotificationManagerService.java.
+ */
+message DNDModeProto {
+ enum Mode {
+ ROOT_CONFIG = -1; // Used to distinguish the config (one per user) from the rules.
+ ZEN_MODE_OFF = 0;
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
+ ZEN_MODE_NO_INTERRUPTIONS = 2;
+ ZEN_MODE_ALARMS = 3;
+ }
+ optional int32 user = 1; // Android user ID (0, 1, 10, ...)
+ optional bool enabled = 2; // true for ROOT_CONFIG if a manualRule is enabled
+ optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG
+ optional Mode zen_mode = 4;
+ // id is one of the system default rule IDs, or empty
+ // May also be "MANUAL_RULE" to indicate app-activation of the manual rule.
+ optional string id = 5;
+ optional int32 uid = 6 [(is_uid) = true]; // currently only SYSTEM_UID or 0 for other
+ optional DNDPolicyProto policy = 7;
+}
+
+/**
+ * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto.
+ */
+message DNDPolicyProto {
+ enum State {
+ STATE_UNSET = 0;
+ STATE_ALLOW = 1;
+ STATE_DISALLOW = 2;
+ }
+ optional State calls = 1;
+ optional State repeat_callers = 2;
+ optional State messages = 3;
+ optional State conversations = 4;
+ optional State reminders = 5;
+ optional State events = 6;
+ optional State alarms = 7;
+ optional State media = 8;
+ optional State system = 9;
+ optional State fullscreen = 10;
+ optional State lights = 11;
+ optional State peek = 12;
+ optional State status_bar = 13;
+ optional State badge = 14;
+ optional State ambient = 15;
+ optional State notification_list = 16;
+
+ enum PeopleType {
+ PEOPLE_UNSET = 0;
+ PEOPLE_ANYONE = 1;
+ PEOPLE_CONTACTS = 2;
+ PEOPLE_STARRED = 3;
+ PEOPLE_NONE = 4;
+ }
+
+ optional PeopleType allow_calls_from = 17;
+ optional PeopleType allow_messages_from = 18;
+
+ enum ConversationType {
+ CONV_UNSET = 0;
+ CONV_ANYONE = 1;
+ CONV_IMPORTANT = 2;
+ CONV_NONE = 3;
+ }
+
+ optional ConversationType allow_conversations_from = 19;
+}
+
/**
* Atom that contains a list of a package's channel group preferences, pulled from
* NotificationManagerService.java.
@@ -6309,6 +6559,16 @@ message ContentCaptureServiceEvents {
SET_WHITELIST = 3;
SET_DISABLED = 4;
ON_USER_DATA_REMOVED = 5;
+ ON_DATA_SHARE_REQUEST = 6;
+ ACCEPT_DATA_SHARE_REQUEST = 7;
+ REJECT_DATA_SHARE_REQUEST = 8;
+ DATA_SHARE_WRITE_FINISHED = 9;
+ DATA_SHARE_ERROR_IOEXCEPTION = 10;
+ DATA_SHARE_ERROR_EMPTY_DATA = 11;
+ DATA_SHARE_ERROR_CLIENT_PIPE_FAIL = 12;
+ DATA_SHARE_ERROR_SERVICE_PIPE_FAIL = 13;
+ DATA_SHARE_ERROR_CONCURRENT_REQUEST = 14;
+ DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED = 15;
}
optional Event event = 1;
// component/package of content capture service.
@@ -6679,6 +6939,24 @@ message AppCompacted {
optional int64 after_zram_free_kilobytes = 18;
}
+/**
+ * Logs when a Tethering event occurs.
+ *
+ */
+message NetworkTetheringReported {
+ // tethering error code
+ optional android.stats.connectivity.ErrorCode error_code = 1;
+
+ // tethering downstream type
+ optional android.stats.connectivity.DownstreamType downstream_type = 2;
+
+ // transport type of upstream network
+ optional android.stats.connectivity.UpstreamType upstream_type = 3;
+
+ // The user type of Tethering
+ optional android.stats.connectivity.UserType user_type= 4;
+}
+
/**
* Logs a DNS lookup operation initiated by the system resolver on behalf of an application
* invoking native APIs such as getaddrinfo() or Java APIs such as Network#getAllByName().
@@ -6716,6 +6994,172 @@ message NetworkDnsEventReported {
optional int32 sampling_rate_denom = 9;
}
+/**
+ * logs the CapportApiData info
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+ */
+message CapportApiData {
+ // The TTL of the network connection provided by captive portal
+ optional int32 remaining_ttl_secs = 1;
+
+ // The limit traffic data of the network connection provided by captive portal
+ optional int32 remaining_bytes = 2;
+
+ // Is portal url option included in the DHCP packet (Yes, No)
+ optional bool has_portal_url = 3;
+
+ // Is venue info (e.g. store info, maps, flight status) included (Yes, No)
+ optional bool has_venue_info = 4;
+}
+
+/**
+ * logs a network Probe Event
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+ */
+message ProbeEvent {
+ // The probe type (http or https, or captive portal API...)
+ optional android.stats.connectivity.ProbeType probe_type = 1;
+
+ // The latency in microseconds of the probe event
+ optional int32 latency_micros = 2;
+
+ // The result of the probe event
+ optional android.stats.connectivity.ProbeResult probe_result = 3;
+
+ // The CaptivePortal API info
+ optional CapportApiData capport_api_data = 4;
+}
+
+/**
+ * log each ProbeEvent in ProbeEvents
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+ */
+message ProbeEvents {
+ // Record probe event during the validation
+ repeated ProbeEvent probe_event = 1;
+}
+
+/**
+ * The DHCP (Dynamic Host Configuration Protocol) session info
+ * Logged from:
+ * packages/modules/NetworkStack/src/android/net/dhcp/DhcpClient.java
+ */
+message DhcpSession {
+ // The DHCP Feature(s) enabled in this session
+ repeated android.stats.connectivity.DhcpFeature used_features = 1;
+
+ // The discover packet (re)transmit count
+ optional int32 discover_count = 2;
+
+ // The request packet (re)transmit count
+ optional int32 request_count = 3;
+
+ // The IPv4 address conflict count
+ // (only be meaningful when duplicate address detection is enabled)
+ optional int32 conflict_count = 4;
+
+ // The DHCP packet parsing error code in this session
+ // (defined in android.net.metrics.DhcpErrorEvent)
+ repeated android.stats.connectivity.DhcpErrorCode error_code = 5;
+
+ // The result of DHCP hostname transliteration
+ optional android.stats.connectivity.HostnameTransResult ht_result = 6;
+}
+
+/**
+ * Logs Network IP provisioning event
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/networkstack/metrics/NetworkIpProvisioningMetrics.java
+ */
+message NetworkIpProvisioningReported {
+ // Transport type (WIFI, CELLULAR, BLUETOOTH, ..)
+ optional android.stats.connectivity.TransportType transport_type = 1;
+
+ // The latency in microseconds of IP Provisioning over IPV4
+ optional int32 ipv4_latency_micros = 2;
+
+ // The latency in microseconds of IP Provisioning over IPV6
+ optional int32 ipv6_latency_micros = 3;
+
+ // The time duration between provisioning start and end (success or failure)
+ optional int64 provisioning_duration_micros = 4;
+
+ // The specific disconnect reason for this IP provisioning
+ optional android.stats.connectivity.DisconnectCode disconnect_code = 5;
+
+ // Log DHCP session info (Only valid for IPv4)
+ optional DhcpSession dhcp_session = 6 [(log_mode) = MODE_BYTES];
+
+ // The random number between 0 ~ 999 for sampling
+ optional int32 random_number = 7;
+}
+
+/**
+ * Logs Network DHCP Renew event
+ * Logged from:
+ * packages/modules/NetworkStack/src/android/net/dhcp/DhcpClient.java
+ */
+message NetworkDhcpRenewReported {
+ // Transport type (WIFI, CELLULAR, BLUETOOTH, ..)
+ optional android.stats.connectivity.TransportType transport_type = 1;
+
+ // The request packet (re)transmit count
+ optional int32 request_count = 2;
+
+ // The latency in microseconds of DHCP Renew
+ optional int32 latency_micros = 3;
+
+ // The DHCP error code is defined in android.net.metrics.DhcpErrorEvent
+ optional android.stats.connectivity.DhcpErrorCode error_code = 4;
+
+ // The result of DHCP renew
+ optional android.stats.connectivity.DhcpRenewResult renew_result = 5;
+
+ // The random number between 0 ~ 999 for sampling
+ optional int32 random_number = 6;
+}
+
+/**
+ * Logs Network Validation event
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+ */
+message NetworkValidationReported {
+ // Transport type (WIFI, CELLULAR, BLUETOOTH, ..)
+ optional android.stats.connectivity.TransportType transport_type = 1;
+
+ // Record each probe event
+ optional ProbeEvents probe_events = 2 [(log_mode) = MODE_BYTES];
+
+ // The result of the network validation
+ optional android.stats.connectivity.ValidationResult validation_result = 3;
+
+ // The latency in microseconds of network validation
+ optional int32 latency_micros = 4;
+
+ // The validation index (the first validation attempt or second, third...)
+ optional int32 validation_index = 5;
+
+ // The random number between 0 ~ 999 for sampling
+ optional int32 random_number = 6;
+}
+
+/**
+ * Logs NetworkStack Quirk event
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/networkstack/
+ */
+message NetworkStackQuirkReported {
+ // Transport type (WIFI, CELLULAR, BLUETOOTH, ..)
+ optional android.stats.connectivity.TransportType transport_type = 1;
+
+ // Record each Quirk event
+ optional android.stats.connectivity.NetworkQuirkEvent event = 2;
+}
+
/**
* Logs when a data stall event occurs.
*
@@ -7991,6 +8435,245 @@ message CarPowerStateChanged {
optional State state = 1;
}
+/**
+ * Logs when Car User Hal is requested to switch/create/remove user.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalModifyUserRequestReported {
+ // Request id for the request.
+ optional int32 request_id = 1;
+ // Request type.
+ enum RequestType {
+ UNKNOWN = 0;
+ // Car user manager requested user switch.
+ SWITCH_REQUEST_ANDROID = 1;
+ // OEM requested User switch.
+ SWITCH_REQUEST_OEM = 2;
+ // Hal switch requested after android switch using activity manager.
+ SWITCH_REQUEST_LEGACY = 3;
+ // Create User
+ CREATE_REQUEST = 4;
+ // Remove User
+ REMOVE_REQUEST = 5;
+ }
+ optional RequestType request_type = 2;
+ // Android User id of the current user which can only be 0, 10, 11 and so on.
+ // -1 if not available.
+ optional int32 user_id = 3;
+ // VHAL flags of the current user. (-1 if not available)
+ optional int32 user_flags = 4;
+ // Android User id of the target user for switch/create/remove. It can only
+ // be 0, 10, 11 and so on. -1 if not available.
+ optional int32 target_user_id = 5;
+ // VHAL flags of the target user for switch/create/remove. (-1 if not available)
+ optional int32 target_user_flags = 6;
+ // Request timeout Milliseconds (-1 if not available)
+ optional int32 timeout_millis = 7;
+}
+
+/**
+ * Logs when Car User Hal responds to switch/create user request.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalModifyUserResponseReported {
+ // Request id of the request associated with the response.
+ optional int32 request_id = 1;
+ // Car user hal callback status.
+ enum CallbackStatus {
+ UNKNOWN = 0;
+ // Hal response was invalid.
+ INVALID = 1;
+ // Hal response was ok.
+ OK = 2;
+ // Hal timeout during set call.
+ HAL_SET_TIMEOUT = 3;
+ // Hal response timeout.
+ HAL_RESPONSE_TIMEOUT = 4;
+ // Hal responded with wrong info.
+ WRONG_HAL_RESPONSE = 5;
+ // Hal is processing multiple requests simultaneously.
+ CONCURRENT_OPERATION = 6;
+ }
+ optional CallbackStatus callback_status = 2;
+
+ // Hal request status for user switch/create/remove.
+ enum HalRequestStatus {
+ UNSPECIFIED = 0;
+ // Hal request for user switch/create is successful.
+ SUCCESS = 1;
+ // Hal request for user switch/create failed.
+ FAILURE = 2;
+ }
+ optional HalRequestStatus request_status = 3;
+}
+
+/**
+ * Logs when post switch response is posted to Car User Hal.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalPostSwitchResponseReported {
+ // Request id.
+ optional int32 request_id = 1;
+
+ // Android user switch status.
+ enum UserSwitchStatus {
+ UNKNOWN = 0;
+ // Android user switch is successful.
+ SUCCESS = 1;
+ // Android user switch failed.
+ FAILURE = 2;
+ }
+ optional UserSwitchStatus switch_status = 2;
+}
+
+/**
+ * Logs when initial user information is requested from Car User Hal.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalInitialUserInfoRequestReported {
+ // Request id for the request.
+ optional int32 request_id = 1;
+
+ // Request type for initial user information.
+ enum InitialUserInfoRequestType {
+ UNKNOWN = 0;
+ // At the first time Android was booted (or after a factory reset).
+ FIRST_BOOT = 1;
+ // At the first time Android was booted after the system was updated.
+ FIRST_BOOT_AFTER_OTA = 2;
+ // When Android was booted "from scratch".
+ COLD_BOOT = 3;
+ // When Android was resumed after the system was suspended to memory.
+ RESUME = 4;
+ }
+ optional InitialUserInfoRequestType request_type = 2;
+ // Request timeout Milliseconds (-1 if not available)
+ optional int32 timeout_millis = 3;
+}
+
+/**
+ * Logs when Car User Hal responds to initial user information requests.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalInitialUserInfoResponseReported {
+ // Request id of the request associated with the response.
+ optional int32 request_id = 1;
+ // Car user hal callback status.
+ enum CallbackStatus {
+ UNKNOWN = 0;
+ // Hal response was invalid.
+ INVALID = 1;
+ // Hal response was ok.
+ OK = 2;
+ // Hal timeout during set call.
+ HAL_SET_TIMEOUT = 3;
+ // Hal response timeout.
+ HAL_RESPONSE_TIMEOUT = 4;
+ // Hal responded with wrong info.
+ WRONG_HAL_RESPONSE = 5;
+ // Hal is processing multiple requests simultaneously.
+ CONCURRENT_OPERATION = 6;
+ }
+ optional CallbackStatus callback_status = 2;
+ // Response for initial user information request.
+ enum InitialUserInfoResponseAction {
+ UNSPECIFIED = 0;
+ // Let the Android System decide what to do.
+ DEFAULT = 1;
+ // Switch to an existing Android user.
+ SWITCH = 2;
+ // Create a new Android user (and switch to it).
+ CREATE = 3;
+ }
+ optional InitialUserInfoResponseAction response_action = 3;
+ // Android User id of the target user which can only be 0, 10, 11 and so on.
+ // -1 if not available.
+ optional int32 target_user = 4;
+ // VHAL flags of the current user. (-1 if not available)
+ optional int32 target_user_flags = 5;
+ // User locales
+ optional string user_locales = 6;
+}
+
+/**
+ * Logs when set user association is requested from Car User Hal.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalUserAssociationRequestReported {
+ // Request id for the request.
+ optional int32 request_id = 1;
+ // Request type.
+ enum RequestType {
+ UNKNOWN = 0;
+ // For setting user association information.
+ SET = 1;
+ // For getting user association information.
+ GET = 2;
+ }
+ optional RequestType request_type = 2;
+ // Android User id of the current user which can only be 0, 10, 11 and so on.
+ // -1 if not available.
+ optional int32 current_user_id = 3;
+ // VHAL flags of the current user. (-1 if not available)
+ optional int32 current_user_flags = 4;
+ // Number of the set associations requested.
+ optional int32 number_associations = 5;
+ // Concatenated string for the types from set associations request.
+ // This is a string converted from an array of integers.
+ optional string user_identification_association_types = 6;
+ // Concatenated string for the values from set associations request.
+ // This is a string converted from an array of integers.
+ optional string user_identification_association_values = 7;
+}
+
+/**
+ * Logs when Car User Hal responds to set user association requests.
+ *
+ * Logged from:
+ * packages/services/Car/service/src/com/android/car/hal/UserHalService.java
+ */
+message CarUserHalSetUserAssociationResponseReported {
+ // Request id of the request associated with the response.
+ optional int32 request_id = 1;
+ // Car user hal callback status.
+ enum CallbackStatus {
+ UNKNOWN = 0;
+ // Hal response was invalid.
+ INVALID = 1;
+ // Hal response was ok.
+ OK = 2;
+ // Hal timeout during set call.
+ HAL_SET_TIMEOUT = 3;
+ // Hal response timeout.
+ HAL_RESPONSE_TIMEOUT = 4;
+ // Hal responded with wrong info.
+ WRONG_HAL_RESPONSE = 5;
+ // Hal is processing multiple requests simultaneously.
+ CONCURRENT_OPERATION = 6;
+ }
+ optional CallbackStatus callback_status = 2;
+ // Number of the set associations in the response.
+ optional int32 number_associations = 3;
+ // Concatenated string for the types from set associations request.
+ // This is a string converted from an array of integers.
+ optional string user_identification_association_types = 4;
+ // Concatenated string for the values from set associations request.
+ // This is a string converted from an array of integers.
+ optional string user_identification_association_values = 5;
+}
+
/**
* Logs whether GarageMode is entered.
*
@@ -9042,6 +9725,7 @@ message RuntimeAppOpAccess {
UNIFORM = 1;
RARELY_USED = 2;
BOOT_TIME_SAMPLING = 3;
+ UNIFORM_OPS = 4;
}
// sampling strategy used to collect this message
@@ -9134,8 +9818,10 @@ message IntegrityRulesPushed {
/**
* Logs when a cell broadcast message is received on the device.
*
- * Logged from CellBroadcastService module:
+ * Logged from Cell Broadcast module and platform:
* packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ * packages/apps/CellBroadcastReceiver/
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
*/
message CellBroadcastMessageReported {
// The type of Cell Broadcast message
@@ -9146,8 +9832,40 @@ message CellBroadcastMessageReported {
CDMA_SPC = 3;
}
+ // The parts of the cell broadcast message pipeline
+ enum ReportSource {
+ UNKNOWN_SOURCE = 0;
+ FRAMEWORK = 1;
+ CB_SERVICE = 2;
+ CB_RECEIVER_APP = 3;
+ }
+
// GSM, CDMA, CDMA-SCP
optional CbType type = 1;
+
+ // The source of the report
+ optional ReportSource source = 2;
+}
+
+/**
+ * Logs when a cell broadcast message is filtered out, or otherwise intentionally not sent to CBR.
+ *
+ * Logged from CellBroadcastService module:
+ * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ */
+message CellBroadcastMessageFiltered {
+ enum FilterReason {
+ NOT_FILTERED = 0;
+ DUPLICATE_MESSAGE = 1;
+ GEOFENCED_MESSAGE = 2;
+ AREA_INFO_MESSAGE = 3;
+ }
+
+ // GSM, CDMA, CDMA-SCP
+ optional CellBroadcastMessageReported.CbType type = 1;
+
+ // The source of the report
+ optional FilterReason filter = 2;
}
/**
@@ -9174,6 +9892,7 @@ message CellBroadcastMessageError {
UNEXPECTED_GSM_MESSAGE_TYPE_FROM_FWK = 12;
UNEXPECTED_CDMA_MESSAGE_TYPE_FROM_FWK = 13;
UNEXPECTED_CDMA_SCP_MESSAGE_TYPE_FROM_FWK = 14;
+ NO_CONNECTION_TO_CB_SERVICE = 15;
}
// What kind of error occurred
@@ -9205,6 +9924,100 @@ message TvTunerStateChanged {
// new state
optional State state = 2;
}
+
+/**
+ * Logs the status of a dvr playback or record.
+ * This is atom ID 279.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/tv/tuner/dvr
+ */
+message TvTunerDvrStatus {
+ enum Type {
+ UNKNOWN_TYPE = 0;
+ PLAYBACK = 1; // is a playback
+ RECORD = 2; // is a record
+ }
+ enum State {
+ UNKNOWN_STATE = 0;
+ STARTED = 1; // DVR is started
+ STOPPED = 2; // DVR is stopped
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // DVR type
+ optional Type type = 2;
+ // DVR state
+ optional State state = 3;
+ // Identify the segment of a record or playback
+ optional int32 segment_id = 4;
+ // indicate how many overflow or underflow happened between started to stopped
+ optional int32 overflow_underflow_count = 5;
+}
+
+/**
+ * Logs when a cas session opened through MediaCas.
+ * This is atom ID 280.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/MediaCas.java
+ */
+message TvCasSessionOpenStatus {
+ enum State {
+ UNKNOWN = 0;
+ SUCCEEDED = 1; // indicate that the session is opened successfully.
+ FAILED = 2; // indicate that the session isn’t opened successfully.
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Cas system Id
+ optional int32 cas_system_id = 2;
+ // State of the session
+ optional State state = 3;
+}
+
+/**
+ * Logs for ContactsProvider general usage.
+ * This is atom ID 301.
+ *
+ * Logged from:
+ * packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java
+ */
+message ContactsProviderStatusReported {
+ enum ApiType {
+ UNKNOWN_API = 0;
+ QUERY = 1;
+ // INSERT includes insert and bulkInsert, and inserts triggered by applyBatch.
+ INSERT = 2;
+ // UPDATE and DELETE includes update/delete and the ones triggered by applyBatch.
+ UPDATE = 3;
+ DELETE = 4;
+ }
+
+ enum ResultType {
+ UNKNOWN_RESULT = 0;
+ SUCCESS = 1;
+ FAIL = 2;
+ ILLEGAL_ARGUMENT = 3;
+ UNSUPPORTED_OPERATION = 4;
+ }
+
+ enum CallerType {
+ UNSPECIFIED_CALLER_TYPE = 0;
+ CALLER_IS_SYNC_ADAPTER = 1;
+ CALLER_IS_NOT_SYNC_ADAPTER = 2;
+ }
+
+ optional ApiType api_type = 1;
+ // Defined in
+ // packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java
+ optional int32 uri_type = 2;
+ optional CallerType caller_type = 3;
+ optional ResultType result_type = 4;
+ optional int32 result_count = 5;
+ optional int64 latency_micros = 6;
+}
+
/**
* Logs when an app is frozen or unfrozen.
*
@@ -9398,7 +10211,7 @@ message GnssStats {
optional int64 time_to_first_fix_reports = 3;
// Total pulled reported time to first fix (in milli-seconds) since boot
- optional int64 time_to_first_fix_milli_s = 4;
+ optional int64 time_to_first_fix_millis = 4;
// Number of position accuracy reports since boot
optional int64 position_accuracy_reports = 5;
@@ -9721,15 +10534,20 @@ message AccessibilityServiceReported {
optional android.stats.accessibility.ServiceStatus service_status = 2;
}
-message DisplayWakeReason {
+/**
+ * Logs when display wake up.
+ *
+ * Logged from:
+ * services/core/java/com/android/server/power/Notifier.java
+ */
+
+message DisplayWakeReported {
// Wake_up_reason code
// If LOWORD(wake_up_reason) = 0
// reference to HIWORD(wake_up_reason) PowerManager.WAKE_REASON_XXX
// else reference wake_up_reason to
- // frameworks/base/services/core/java/com/android/server/power/Notifier.java#DispWakeupReason
+ // services/core/java/com/android/server/power/Notifier.java#onWakeUp
optional int32 wake_up_reason = 1;
- // Count of wake up by reason
- optional int32 wake_times = 2;
}
/**
@@ -9903,3 +10721,453 @@ message MediaOutputOpSwitchReported {
// The amount of applied devices within a remote dynamic group after a switching is done.
optional int32 applied_device_count_within_remote_group = 9;
}
+
+/**
+ * Logs when the Assistant is invoked.
+ *
+ * Logged from:
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+ */
+message AssistantInvocationReported {
+
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+
+ // The registered Assistant's uid and package (as for UiEventReported).
+ optional int32 uid = 2 [(is_uid) = true];
+ optional string package_name = 3;
+
+ // An identifier used to disambiguate which logs refer to a particular invocation of the
+ // Assistant (as for UiEventReported).
+ optional int32 instance_id = 4;
+
+ // The state of the device at the time of invocation.
+ enum DeviceState {
+ UNKNOWN_DEVICE_STATE = 0;
+ AOD1 = 1;
+ AOD2 = 2;
+ BOUNCER = 3;
+ UNLOCKED_LOCKSCREEN = 4;
+ LAUNCHER_HOME = 5;
+ LAUNCHER_OVERVIEW = 6;
+ LAUNCHER_ALL_APPS = 7;
+ APP_DEFAULT = 8;
+ APP_IMMERSIVE = 9;
+ APP_FULLSCREEN = 10;
+ }
+ optional DeviceState device_state = 5;
+
+ // Whether the Assistant handles were showing at the time of invocation.
+ optional bool assistant_handles_showing = 6;
+}
+
+/**
+ * Logs when an AudioRecord finishes running on an audio device
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioRecordDeviceUsageReported {
+ // The devices connected to this AudioRecord.
+ // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+ // See lookup() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string devices = 1;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 2;
+
+ // The amount of time spent in the device as measured by the active track in AudioFlinger.
+ optional int64 device_time_nanos = 3;
+
+ // The audio data format used for encoding.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+ optional string encoding = 4;
+
+ // The client-server buffer framecount.
+ // The framecount is generally between 960 - 48000 for PCM encoding.
+ // The framecount represents raw buffer size in bytes for non-PCM encoding.
+ optional int32 frame_count = 5;
+
+ // The number of audio intervals (contiguous, continuous playbacks).
+ optional int32 interval_count = 6;
+
+ // The sample rate of the AudioRecord.
+ // A number generally between 8000-96000 (frames per second).
+ optional int32 sample_rate = 7;
+
+ // The audio input flags used to construct the AudioRecord.
+ // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t
+ optional string flags = 8;
+
+ // The santized package name of the audio client associated with the AudioRecord.
+ // See getSanitizedPackageNameAndVersionCode() in
+ // frameworks/av/services/mediametrics/MediaMetricsService.cpp
+ optional string package_name = 9;
+
+ // The selected device id (nonzero if a non-default device is selected)
+ optional int32 selected_device_id = 10;
+
+ // The caller of the AudioRecord.
+ // See lookup() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ optional string caller = 11;
+
+ // The audio source for AudioRecord.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_source_t
+ optional string source = 12;
+}
+
+/**
+ * Logs when an AudioThread finishes running on an audio device
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioThreadDeviceUsageReported {
+ // The devices connected to this audio thread.
+ // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+ // (for record threads):
+ // See lookup in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // (for playback threads):
+ // See lookup() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string devices = 1;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 2;
+
+ // The amount of time spent in the device as measured by the active track in AudioFlinger.
+ optional int64 device_time_nanos = 3;
+
+ // The audio data format used for encoding.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+ optional string encoding = 4;
+
+ // The framecount of the buffer delivered to (or from) the HAL.
+ // The framecount is generally ~960 for PCM encoding.
+ // The framecount represents raw buffer size in bytes for non-PCM encoding.
+ optional int32 frame_count = 5;
+
+ // The number of audio intervals (contiguous, continuous playbacks).
+ optional int32 interval_count = 6;
+
+ // The sample rate of the audio thread.
+ // A number generally between 8000-96000 (frames per second).
+ optional int32 sample_rate = 7;
+
+ // The audio flags used to construct the thread
+ // (for record threads):
+ // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t
+ // (for playback threads):
+ // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t
+ optional string flags = 8;
+
+ // The number of underruns encountered for a playback thread or the
+ // number of overruns encountered for a capture thread.
+ optional int32 xruns = 9;
+
+ // The type of thread
+ // A thread type enumeration from
+ // frameworks/av/mediametrics/services/Translate.h
+ optional string type = 10;
+}
+
+/**
+ * Logs when an AudioTrack finishes running on an audio device
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioTrackDeviceUsageReported {
+ // The output devices connected to this AudioTrack.
+ // A string OR of various output device categories, e.g. "DEVICE1|DEVICE2".
+ // See lookup() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string devices = 1;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 2;
+
+ // The amount of time spent in the device as measured by the active track in AudioFlinger.
+ optional int64 device_time_nanos = 3;
+
+ // The audio data format used for encoding.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+ optional string encoding = 4;
+
+ // The client-server buffer framecount.
+ // The framecount is generally between 960 - 48000 for PCM encoding.
+ // The framecount represents raw buffer size in bytes for non-PCM encoding.
+ // A static track (see traits) may have a very large framecount.
+ optional int32 frame_count = 5;
+
+ // The number of audio intervals (contiguous, continuous playbacks).
+ optional int32 interval_count = 6;
+
+ // The sample rate of the AudioTrack.
+ // A number generally between 8000-96000 (frames per second).
+ optional int32 sample_rate = 7;
+
+ // The audio flags used to construct the AudioTrack.
+ // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t
+ optional string flags = 8;
+
+ // The number of underruns encountered.
+ optional int32 xruns = 9;
+
+ // The santized package name of the audio client associated with the AudioTrack.
+ // See getSanitizedPackageNameAndVersionCode() in
+ // frameworks/av/services/mediametrics/MediaMetricsService.cpp
+ optional string package_name = 10;
+
+ // The latency of the last sample in the buffer in milliseconds.
+ optional float device_latency_millis = 11;
+
+ // The startup time in milliseconds from start() to sample played.
+ optional float device_startup_millis = 12;
+
+ // The average volume of the track on the device [ 0.f - 1.f ]
+ optional float device_volume = 13;
+
+ // The selected device id (nonzero if a non-default device is selected)
+ optional int32 selected_device_id = 14;
+
+ // The stream_type category for the AudioTrack.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_stream_type_t
+ optional string stream_type = 15;
+
+ // The usage for the AudioTrack.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_usage_t
+ optional string usage = 16;
+
+ // The content type of the AudioTrack.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_content_type_t
+ optional string content_type = 17;
+
+ // The caller of the AudioTrack.
+ // See lookup() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ optional string caller = 18;
+
+ // The traits of the AudioTrack.
+ // A string OR of different traits, may be empty string.
+ // Only "static" is supported for R.
+ // See lookup() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ optional string traits = 19;
+}
+
+/**
+ * Logs the status of an audio device connection attempt.
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioDeviceConnectionReported {
+ // The input devices represented by this report.
+ // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+ // See lookup() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string input_devices = 1;
+
+ // The output devices represented by this report.
+ // A string OR of various output device categories.
+ // See lookup() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string output_devices = 2;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 3;
+
+ // The result of the audio device connection.
+ // 0 indicates success: connection verified.
+ // 1 indicates unknown: connection not verified or not known if diverted properly.
+ // Other values indicate specific status.
+ // See DeviceConnectionResult in frameworks/av/services/mediametrics/AudioTypes.h
+ optional int32 result = 4;
+
+ // Average milliseconds of time to connect
+ optional float time_to_connect_millis = 5;
+
+ // Number of connections if aggregated statistics, otherwise 1.
+ optional int32 connection_count = 6;
+}
+
+/**
+ * Logs: i) creation of different types of cryptographic keys in the keystore,
+ * ii) operations performed using the keys,
+ * iii) attestation of the keys
+ * Logged from: system/security/keystore/key_event_log_handler.cpp
+ */
+message KeystoreKeyEventReported {
+
+ enum Algorithm {
+ /** Asymmetric algorithms. */
+ RSA = 1;
+ // 2 removed, do not reuse.
+ EC = 3;
+ /** Block cipher algorithms */
+ AES = 32;
+ TRIPLE_DES = 33;
+ /** MAC algorithms */
+ HMAC = 128;
+ };
+ /** Algorithm associated with the key */
+ optional Algorithm algorithm = 1;
+
+ /** Size of the key */
+ optional int32 key_size = 2;
+
+ enum KeyOrigin {
+ /** Generated in keymaster. Should not exist outside the TEE. */
+ GENERATED = 0;
+ /** Derived inside keymaster. Likely exists off-device. */
+ DERIVED = 1;
+ /** Imported into keymaster. Existed as cleartext in Android. */
+ IMPORTED = 2;
+ /** Keymaster did not record origin. */
+ UNKNOWN = 3;
+ /** Securely imported into Keymaster. */
+ SECURELY_IMPORTED = 4;
+ };
+ /* Logs whether the key was generated, imported, securely imported, or derived.*/
+ optional KeyOrigin key_origin = 3;
+
+ enum HardwareAuthenticatorType {
+ NONE = 0;
+ PASSWORD = 1;
+ FINGERPRINT = 2;
+ // Additional entries must be powers of 2.
+ };
+ /**
+ * What auth types does this key require? If none,
+ * then no auth required.
+ */
+ optional HardwareAuthenticatorType user_auth_type = 4;
+
+ /**
+ * If user authentication is required, is the requirement time based? If it
+ * is not time based then this field will not be used and the key is per
+ * operation. Per operation keys must be user authenticated on each usage.
+ */
+ optional int32 user_auth_key_timeout_secs = 5;
+
+ /**
+ * padding mode, digest, block_mode and purpose should ideally be repeated
+ * fields. However, since statsd does not support repeated fields in
+ * pushed atoms, they are represented using bitmaps.
+ */
+
+ /** Track which padding mode is being used.*/
+ optional int32 padding_mode_bitmap = 6;
+
+ /** Track which digest is being used. */
+ optional int32 digest_bitmap = 7;
+
+ /** Track what block mode is being used (for encryption). */
+ optional int32 block_mode_bitmap = 8;
+
+ /** Track what purpose is this key serving. */
+ optional int32 purpose_bitmap = 9;
+
+ enum EcCurve {
+ P_224 = 0;
+ P_256 = 1;
+ P_384 = 2;
+ P_521 = 3;
+ };
+ /** Which ec curve was selected if elliptic curve cryptography is in use **/
+ optional EcCurve ec_curve = 10;
+
+ enum KeyBlobUsageRequirements {
+ STANDALONE = 0;
+ REQUIRES_FILE_SYSTEM = 1;
+ };
+ /** Standalone or is a file system required */
+ optional KeyBlobUsageRequirements key_blob_usage_reqs = 11;
+
+ enum Type {
+ key_operation = 0;
+ key_creation = 1;
+ key_attestation = 2;
+ }
+ /** Key creation event, operation event or attestation event? */
+ optional Type type = 12;
+
+ /** Was the key creation, operation, or attestation successful? */
+ optional bool was_successful = 13;
+
+ /** Response code or error code */
+ optional int32 error_code = 14;
+}
+
+// Blob Committer stats
+// Keep in sync between:
+// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+// frameworks/base/cmds/statsd/src/atoms.proto
+message BlobCommitterProto {
+ // Committer app's uid
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Unix epoch timestamp of the commit in milliseconds
+ optional int64 commit_timestamp_millis = 2;
+
+ // Flags of what access types the committer has set for the Blob
+ optional int32 access_mode = 3;
+
+ // Number of packages that have been whitelisted for ACCESS_TYPE_WHITELIST
+ optional int32 num_whitelisted_package = 4;
+}
+
+// Blob Leasee stats
+// Keep in sync between:
+// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+// frameworks/base/cmds/statsd/src/atoms.proto
+message BlobLeaseeProto {
+ // Leasee app's uid
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Unix epoch timestamp for lease expiration in milliseconds
+ optional int64 lease_expiry_timestamp_millis = 2;
+}
+
+// List of Blob Committers
+// Keep in sync between:
+// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+// frameworks/base/cmds/statsd/src/atoms.proto
+message BlobCommitterListProto {
+ repeated BlobCommitterProto committer = 1;
+}
+
+// List of Blob Leasees
+// Keep in sync between:
+// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+// frameworks/base/cmds/statsd/src/atoms.proto
+message BlobLeaseeListProto {
+ repeated BlobLeaseeProto leasee = 1;
+}
+
+/**
+ * Logs the current state of a Blob committed with BlobStoreManager
+ *
+ * Pulled from:
+ * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+ */
+message BlobInfo {
+ // Id of the Blob
+ optional int64 blob_id = 1;
+
+ // Size of the Blob data
+ optional int64 size = 2;
+
+ // Unix epoch timestamp of the Blob's expiration in milliseconds
+ optional int64 expiry_timestamp_millis = 3;
+
+ // List of committers of this Blob
+ optional BlobCommitterListProto committers = 4;
+
+ // List of leasees of this Blob
+ optional BlobLeaseeListProto leasees = 5;
+}
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 9df4d1f8ce24af8cee201538fe3dba9767566400..bb5d0a6bab58ddfad807746a9477ee5ba1ba3b4f 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -44,7 +44,8 @@ StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_
bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector>* data) {
lock_guard lock(mLock);
- int64_t elapsedTimeNs = getElapsedRealtimeNs();
+ const int64_t elapsedTimeNs = getElapsedRealtimeNs();
+ const int64_t systemUptimeMillis = getSystemUptimeMillis();
StatsdStats::getInstance().notePull(mTagId);
const bool shouldUseCache =
(mLastEventTimeNs == eventTimeNs) || (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs);
@@ -67,16 +68,18 @@ bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector mPullTimeoutNs;
+ const int64_t pullElapsedDurationNs = getElapsedRealtimeNs() - elapsedTimeNs;
+ const int64_t pullSystemUptimeDurationMillis = getSystemUptimeMillis() - systemUptimeMillis;
+ StatsdStats::getInstance().notePullTime(mTagId, pullElapsedDurationNs);
+ const bool pullTimeOut = pullElapsedDurationNs > mPullTimeoutNs;
if (pullTimeOut) {
// Something went wrong. Discard the data.
mCachedData.clear();
mHasGoodData = false;
- StatsdStats::getInstance().notePullTimeout(mTagId);
+ StatsdStats::getInstance().notePullTimeout(
+ mTagId, pullSystemUptimeDurationMillis, NanoToMillis(pullElapsedDurationNs));
ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId,
- (long long)pullDurationNs);
+ (long long)pullElapsedDurationNs);
return mHasGoodData;
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index c027fffd20a0e32205c78dd51496754cfd06de2b..6e89038f415208be27b084b692e3a0c9061d33ec 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -38,6 +38,7 @@ using android::util::ProtoOutputStream;
using std::lock_guard;
using std::shared_ptr;
using std::string;
+using std::to_string;
using std::vector;
const int FIELD_ID_BEGIN_TIME = 1;
@@ -436,9 +437,18 @@ void StatsdStats::notePullDataError(int pullAtomId) {
mPulledAtomStats[pullAtomId].dataError++;
}
-void StatsdStats::notePullTimeout(int pullAtomId) {
+void StatsdStats::notePullTimeout(int pullAtomId,
+ int64_t pullUptimeMillis,
+ int64_t pullElapsedMillis) {
lock_guard lock(mLock);
- mPulledAtomStats[pullAtomId].pullTimeout++;
+ PulledAtomStats& pulledAtomStats = mPulledAtomStats[pullAtomId];
+ pulledAtomStats.pullTimeout++;
+
+ if (pulledAtomStats.pullTimeoutMetadata.size() == kMaxTimestampCount) {
+ pulledAtomStats.pullTimeoutMetadata.pop_front();
+ }
+
+ pulledAtomStats.pullTimeoutMetadata.emplace_back(pullUptimeMillis, pullElapsedMillis);
}
void StatsdStats::notePullExceedMaxDelay(int pullAtomId) {
@@ -630,6 +640,7 @@ void StatsdStats::resetInternalLocked() {
pullStats.second.unregisteredCount = 0;
pullStats.second.atomErrorCount = 0;
pullStats.second.binderCallFailCount = 0;
+ pullStats.second.pullTimeoutMetadata.clear();
}
mAtomMetricStats.clear();
mActivationBroadcastGuardrailStats.clear();
@@ -786,6 +797,20 @@ void StatsdStats::dumpStats(int out) const {
pair.second.pullUidProviderNotFound, pair.second.pullerNotFound,
pair.second.registeredCount, pair.second.unregisteredCount,
pair.second.atomErrorCount);
+ if (pair.second.pullTimeoutMetadata.size() > 0) {
+ string uptimeMillis = "(pull timeout system uptime millis) ";
+ string pullTimeoutMillis = "(pull timeout elapsed time millis) ";
+ for (const auto& stats : pair.second.pullTimeoutMetadata) {
+ uptimeMillis.append(to_string(stats.pullTimeoutUptimeMillis)).append(",");;
+ pullTimeoutMillis.append(to_string(stats.pullTimeoutElapsedMillis)).append(",");
+ }
+ uptimeMillis.pop_back();
+ uptimeMillis.push_back('\n');
+ pullTimeoutMillis.pop_back();
+ pullTimeoutMillis.push_back('\n');
+ dprintf(out, "%s", uptimeMillis.c_str());
+ dprintf(out, "%s", pullTimeoutMillis.c_str());
+ }
}
if (mAnomalyAlarmRegisteredStats > 0) {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 8587e1452543e6fa42de8f5c0ea9724e3804771d..005048446fc31475c47ed57254cdf35d204e0769 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -101,7 +101,7 @@ public:
// Per atom dimension key size limit
static const std::map> kAtomDimensionKeySizeLimitMap;
- const static int kMaxConfigCountPerUid = 10;
+ const static int kMaxConfigCountPerUid = 20;
const static int kMaxAlertCountPerConfig = 100;
const static int kMaxConditionCountPerConfig = 300;
const static int kMaxMetricCountPerConfig = 1000;
@@ -352,7 +352,7 @@ public:
/*
* Records pull exceeds timeout for the puller.
*/
- void notePullTimeout(int pullAtomId);
+ void notePullTimeout(int pullAtomId, int64_t pullUptimeMillis, int64_t pullElapsedMillis);
/*
* Records pull exceeds max delay for a metric.
@@ -498,6 +498,14 @@ public:
*/
void dumpStats(int outFd) const;
+ typedef struct PullTimeoutMetadata {
+ int64_t pullTimeoutUptimeMillis;
+ int64_t pullTimeoutElapsedMillis;
+ PullTimeoutMetadata(int64_t uptimeMillis, int64_t elapsedMillis) :
+ pullTimeoutUptimeMillis(uptimeMillis),
+ pullTimeoutElapsedMillis(elapsedMillis) {/* do nothing */}
+ } PullTimeoutMetadata;
+
typedef struct {
long totalPull = 0;
long totalPullFromCache = 0;
@@ -519,6 +527,7 @@ public:
long unregisteredCount = 0;
int32_t atomErrorCount = 0;
long binderCallFailCount = 0;
+ std::list pullTimeoutMetadata;
} PulledAtomStats;
typedef struct {
@@ -660,6 +669,8 @@ private:
FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
+
+ FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index cdd20cdd70f9cd0c151c1c0aa7242fe29beda025..fe143e496373219a01ac9ff68e2721e609d6fdb0 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -98,7 +98,7 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo
// Stores atom id to primary key pairs for each state atom that the metric is
// sliced by.
- std::map statePrimaryKeys;
+ std::map statePrimaryKeys;
// For states with primary fields, use MetricStateLinks to get the primary
// field values from the log event. These values will form a primary key
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index e8c575a1adea7c6e1b7098b9bf2c90691c8d5af5..60de1a24cce565f72d8beb70f35fc5bf7fed8d4a 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -21,7 +21,6 @@
#include
#include "CountMetricProducer.h"
-#include "atoms_info.h"
#include "condition/CombinationConditionTracker.h"
#include "condition/SimpleConditionTracker.h"
#include "guardrail/StatsdStats.h"
@@ -361,20 +360,17 @@ void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
protoOutput->end(token);
}
- mLastReportTimeNs = dumpTimeStampNs;
- mLastReportWallClockNs = getWallClockNs();
+ // Do not update the timestamps when data is not cleared to avoid timestamps from being
+ // misaligned.
+ if (erase_data) {
+ mLastReportTimeNs = dumpTimeStampNs;
+ mLastReportWallClockNs = getWallClockNs();
+ }
VLOG("=========================Metric Reports End==========================");
}
bool MetricsManager::checkLogCredentials(const LogEvent& event) {
- // TODO(b/154856835): Remove this check once we get whitelist from the config.
- if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) !=
- android::util::AtomsInfo::kWhitelistedAtoms.end())
- {
- return true;
- }
-
if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) {
return true;
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index c0d1174023143f3deedaae2d0376af08645f5f1a..5987a723a421044a4f5c68b2b20e633fa588e506 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -189,11 +189,6 @@ void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d",
(long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
oldState.mValue.int_value, newState.mValue.int_value);
- // If condition is not true or metric is not active, we do not need to pull
- // for this state change.
- if (mCondition != ConditionState::kTrue || !mIsActive) {
- return;
- }
// If old and new states are in the same StateGroup, then we do not need to
// pull for this state change.
@@ -205,6 +200,12 @@ void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
return;
}
+ // If condition is not true or metric is not active, we do not need to pull
+ // for this state change.
+ if (mCondition != ConditionState::kTrue || !mIsActive) {
+ return;
+ }
+
bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs;
if (isEventLate) {
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
@@ -412,7 +413,6 @@ void ValueMetricProducer::resetBase() {
for (auto& slice : mCurrentBaseInfo) {
for (auto& baseInfo : slice.second) {
baseInfo.hasBase = false;
- baseInfo.hasCurrentState = false;
}
}
mHasGlobalBase = false;
@@ -625,7 +625,6 @@ void ValueMetricProducer::accumulateEvents(const std::vectorsecond) {
baseInfo.hasBase = false;
- baseInfo.hasCurrentState = false;
}
}
}
@@ -820,6 +819,8 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(
Interval& interval = intervals[i];
interval.valueIndex = i;
Value value;
+ baseInfo.hasCurrentState = true;
+ baseInfo.currentState = stateKey;
if (!getDoubleOrLong(event, matcher, value)) {
VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
StatsdStats::getInstance().noteBadValueType(mMetricId);
@@ -907,7 +908,6 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(
interval.hasValue = true;
}
interval.sampleSize += 1;
- baseInfo.currentState = stateKey;
}
// Only trigger the tracker if all intervals are correct
@@ -951,6 +951,7 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {
if (mCondition == ConditionState::kUnknown) {
StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
+ invalidateCurrentBucketWithoutResetBase(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN);
}
VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
@@ -959,7 +960,10 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
int64_t bucketEndTime = fullBucketEndTimeNs;
int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
- if (numBucketsForward > 1) {
+
+ // Skip buckets if this is a pulled metric or a pushed metric that is diffed.
+ if (numBucketsForward > 1 && (mIsPulled || mUseDiff)) {
+
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
// Something went wrong. Maybe the device was sleeping for a long time. It is better
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 3de5b99a2b090e7e896c0fc5ea359b1900be5855..b359af745c91fa5c26073354569f8c7b6c8a28b3 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -75,7 +75,7 @@ public:
if (!mSplitBucketForAppUpgrade) {
return;
}
- if (mIsPulled && mCondition) {
+ if (mIsPulled && mCondition == ConditionState::kTrue) {
pullAndMatchEventsLocked(eventTimeNs);
}
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
@@ -84,7 +84,7 @@ public:
// ValueMetric needs special logic if it's a pulled atom.
void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
std::lock_guard lock(mMutex);
- if (mIsPulled && mCondition) {
+ if (mIsPulled && mCondition == ConditionState::kTrue) {
pullAndMatchEventsLocked(eventTimeNs);
}
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
@@ -313,6 +313,7 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestSlicedState);
FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMap);
FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithCondition);
FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 6bfa26761b2f7fff448eac0a07816313d60526c9..ddd2725c9cb976803de60bfbe5b04c3605539fc0 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -467,6 +467,11 @@ message StatsdStatsReport {
optional int64 binder_call_failed = 19;
optional int64 failed_uid_provider_not_found = 20;
optional int64 puller_not_found = 21;
+ message PullTimeoutMetadata {
+ optional int64 pull_timeout_uptime_millis = 1;
+ optional int64 pull_timeout_elapsed_millis = 2;
+ }
+ repeated PullTimeoutMetadata pull_atom_metadata = 22;
}
repeated PulledAtomStats pulled_atom_stats = 10;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index bafdfcba59b2386b67e1fa39f71ead394a69e018..423bae8bc0a41d1f705dbe4d3f59b81d9d77efba 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -81,6 +81,9 @@ const int FIELD_ID_ATOM_ERROR_COUNT = 18;
const int FIELD_ID_BINDER_CALL_FAIL_COUNT = 19;
const int FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND = 20;
const int FIELD_ID_PULLER_NOT_FOUND = 21;
+const int FIELD_ID_PULL_TIMEOUT_METADATA = 22;
+const int FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS = 1;
+const int FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS = 2;
// for AtomMetricStats proto
const int FIELD_ID_ATOM_METRIC_STATS = 17;
@@ -497,6 +500,16 @@ void writePullerStatsToStream(const std::pair
(long long)pair.second.pullUidProviderNotFound);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULLER_NOT_FOUND,
(long long)pair.second.pullerNotFound);
+ for (const auto& pullTimeoutMetadata : pair.second.pullTimeoutMetadata) {
+ uint64_t timeoutMetadataToken = protoOutput->start(FIELD_TYPE_MESSAGE |
+ FIELD_ID_PULL_TIMEOUT_METADATA |
+ FIELD_COUNT_REPEATED);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS,
+ pullTimeoutMetadata.pullTimeoutUptimeMillis);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS,
+ pullTimeoutMetadata.pullTimeoutElapsedMillis);
+ protoOutput->end(timeoutMetadataToken);
+ }
protoOutput->end(token);
}
@@ -542,6 +555,10 @@ int64_t getElapsedRealtimeMillis() {
return ::android::elapsedRealtime();
}
+int64_t getSystemUptimeMillis() {
+ return ::android::uptimeMillis();
+}
+
int64_t getWallClockNs() {
return time(nullptr) * NS_PER_SEC;
}
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index 20d93b5a53654c42efa38aaba82ddea0626e8c2c..eb65dc6979c54b521264474631cdc2c7b6abd6df 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -61,6 +61,9 @@ int64_t getElapsedRealtimeMillis();
// Gets the elapsed timestamp in seconds.
int64_t getElapsedRealtimeSec();
+// Gets the system uptime in millis.
+int64_t getSystemUptimeMillis();
+
// Gets the wall clock timestamp in ns.
int64_t getWallClockNs();
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 72decf2c7bd00604baa446e0876dbde08531a867..acdffd3d4712c7ac983b86d624d33e5508945c35 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -198,6 +198,9 @@ message EventMetric {
optional int64 condition = 3;
repeated MetricConditionLink links = 4;
+
+ reserved 100;
+ reserved 101;
}
message CountMetric {
@@ -218,6 +221,9 @@ message CountMetric {
repeated MetricStateLink state_link = 9;
optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message DurationMetric {
@@ -245,6 +251,9 @@ message DurationMetric {
optional TimeUnit bucket = 7;
optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message GaugeMetric {
@@ -281,6 +290,9 @@ message GaugeMetric {
optional int32 max_pull_delay_sec = 13 [default = 30];
optional bool split_bucket_for_app_upgrade = 14 [default = true];
+
+ reserved 100;
+ reserved 101;
}
message ValueMetric {
@@ -333,6 +345,9 @@ message ValueMetric {
optional bool split_bucket_for_app_upgrade = 17 [default = true];
optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message Alert {
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index 23f8ca4e74e60518bdf7d0e6f55185127a42f54b..a21eb9b9147ff56a2c7842f2d20028dfa194805b 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -33,6 +33,12 @@ namespace android {
namespace os {
namespace statsd {
+// These constants must be kept in sync with those in StatsDimensionsValue.java.
+const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
+const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3;
+const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6;
+const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7;
+
namespace {
void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
const vector& attributionUids, const vector& attributionTags,
@@ -291,34 +297,76 @@ TEST(AtomMatcherTest, TestWriteDimensionPath) {
}
}
-//TODO(b/149050405) Update this test for StatsDimensionValueParcel
-//TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
-// HashableDimensionKey dim;
-//
-// int pos1[] = {1, 1, 1};
-// int pos2[] = {1, 1, 2};
-// int pos3[] = {1, 1, 3};
-// int pos4[] = {2, 0, 0};
-//
-// Field field1(10, pos1, 2);
-// Field field2(10, pos2, 2);
-// Field field3(10, pos3, 2);
-// Field field4(10, pos4, 0);
-//
-// Value value1((int32_t)10025);
-// Value value2("tag");
-// Value value3((int32_t)987654);
-// Value value4((int32_t)99999);
-//
-// dim.addValue(FieldValue(field1, value1));
-// dim.addValue(FieldValue(field2, value2));
-// dim.addValue(FieldValue(field3, value3));
-// dim.addValue(FieldValue(field4, value4));
-//
-// SubscriberReporter::getStatsDimensionsValue(dim);
-// // TODO(b/110562792): can't test anything here because StatsDimensionsValue class doesn't
-// // have any read api.
-//}
+void checkAttributionNodeInDimensionsValueParcel(StatsDimensionsValueParcel& attributionNodeParcel,
+ int32_t nodeDepthInAttributionChain,
+ int32_t uid, string tag) {
+ EXPECT_EQ(attributionNodeParcel.field, nodeDepthInAttributionChain /*position at depth 1*/);
+ ASSERT_EQ(attributionNodeParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE);
+ ASSERT_EQ(attributionNodeParcel.tupleValue.size(), 2);
+
+ StatsDimensionsValueParcel uidParcel = attributionNodeParcel.tupleValue[0];
+ EXPECT_EQ(uidParcel.field, 1 /*position at depth 2*/);
+ EXPECT_EQ(uidParcel.valueType, STATS_DIMENSIONS_VALUE_INT_TYPE);
+ EXPECT_EQ(uidParcel.intValue, uid);
+
+ StatsDimensionsValueParcel tagParcel = attributionNodeParcel.tupleValue[1];
+ EXPECT_EQ(tagParcel.field, 2 /*position at depth 2*/);
+ EXPECT_EQ(tagParcel.valueType, STATS_DIMENSIONS_VALUE_STRING_TYPE);
+ EXPECT_EQ(tagParcel.stringValue, tag);
+}
+
+// Test conversion of a HashableDimensionKey into a StatsDimensionValueParcel
+TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
+ int atomId = 10;
+ // First four fields form an attribution chain
+ int pos1[] = {1, 1, 1};
+ int pos2[] = {1, 1, 2};
+ int pos3[] = {1, 2, 1};
+ int pos4[] = {1, 2, 2};
+ int pos5[] = {2, 1, 1};
+
+ Field field1(atomId, pos1, /*depth=*/2);
+ Field field2(atomId, pos2, /*depth=*/2);
+ Field field3(atomId, pos3, /*depth=*/2);
+ Field field4(atomId, pos4, /*depth=*/2);
+ Field field5(atomId, pos5, /*depth=*/0);
+
+ Value value1((int32_t)1);
+ Value value2("string2");
+ Value value3((int32_t)3);
+ Value value4("string4");
+ Value value5((float)5.0);
+
+ HashableDimensionKey dimensionKey;
+ dimensionKey.addValue(FieldValue(field1, value1));
+ dimensionKey.addValue(FieldValue(field2, value2));
+ dimensionKey.addValue(FieldValue(field3, value3));
+ dimensionKey.addValue(FieldValue(field4, value4));
+ dimensionKey.addValue(FieldValue(field5, value5));
+
+ StatsDimensionsValueParcel rootParcel = dimensionKey.toStatsDimensionsValueParcel();
+ EXPECT_EQ(rootParcel.field, atomId);
+ ASSERT_EQ(rootParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE);
+ ASSERT_EQ(rootParcel.tupleValue.size(), 2);
+
+ // Check that attribution chain is populated correctly
+ StatsDimensionsValueParcel attributionChainParcel = rootParcel.tupleValue[0];
+ EXPECT_EQ(attributionChainParcel.field, 1 /*position at depth 0*/);
+ ASSERT_EQ(attributionChainParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE);
+ ASSERT_EQ(attributionChainParcel.tupleValue.size(), 2);
+ checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[0],
+ /*nodeDepthInAttributionChain=*/1,
+ value1.int_value, value2.str_value);
+ checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[1],
+ /*nodeDepthInAttributionChain=*/2,
+ value3.int_value, value4.str_value);
+
+ // Check that the float is populated correctly
+ StatsDimensionsValueParcel floatParcel = rootParcel.tupleValue[1];
+ EXPECT_EQ(floatParcel.field, 2 /*position at depth 0*/);
+ EXPECT_EQ(floatParcel.valueType, STATS_DIMENSIONS_VALUE_FLOAT_TYPE);
+ EXPECT_EQ(floatParcel.floatValue, value5.float_value);
+}
TEST(AtomMatcherTest, TestWriteDimensionToProto) {
HashableDimensionKey dim;
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 076f32752223e6b28ac116686bd23ff8d9bdeb13..1e6680c4756716269004e15e59a7be4e412f6c9e 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -13,6 +13,11 @@
// limitations under the License.
#include "StatsLogProcessor.h"
+
+#include
+#include
+#include
+
#include "StatsService.h"
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
@@ -20,16 +25,10 @@
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
#include "packages/UidMap.h"
-#include "storage/StorageManager.h"
#include "statslog_statsdtest.h"
-
-#include
-#include
-
+#include "storage/StorageManager.h"
#include "tests/statsd_test_util.h"
-#include
-
using namespace android;
using namespace testing;
using ::ndk::SharedRefBase;
@@ -324,6 +323,41 @@ TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate) {
EXPECT_EQ(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end());
}
+TEST(StatsLogProcessorTest, InvalidConfigRemoved) {
+ // Setup simple config key corresponding to empty config.
+ StatsdStats::getInstance().reset();
+ sp m = new UidMap();
+ sp pullerManager = new StatsPullerManager();
+ m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")},
+ {String16("p1"), String16("p2")}, {String16(""), String16("")});
+ sp anomalyAlarmMonitor;
+ sp subscriberAlarmMonitor;
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [](const ConfigKey& key) { return true; },
+ [](const int&, const vector&) {return true;});
+ ConfigKey key(3, 4);
+ StatsdConfig config = MakeConfig(true);
+ p.OnConfigUpdated(0, key, config);
+ EXPECT_EQ(1, p.mMetricsManagers.size());
+ EXPECT_NE(p.mMetricsManagers.find(key), p.mMetricsManagers.end());
+ // Cannot assert the size of mConfigStats since it is static and does not get cleared on reset.
+ EXPECT_NE(StatsdStats::getInstance().mConfigStats.end(),
+ StatsdStats::getInstance().mConfigStats.find(key));
+ EXPECT_EQ(0, StatsdStats::getInstance().mIceBox.size());
+
+ StatsdConfig invalidConfig = MakeConfig(true);
+ invalidConfig.clear_allowed_log_source();
+ p.OnConfigUpdated(0, key, invalidConfig);
+ EXPECT_EQ(0, p.mMetricsManagers.size());
+ // The current configs should not contain the invalid config.
+ EXPECT_EQ(StatsdStats::getInstance().mConfigStats.end(),
+ StatsdStats::getInstance().mConfigStats.find(key));
+ // Both "config" and "invalidConfig" should be in the icebox.
+ EXPECT_EQ(2, StatsdStats::getInstance().mIceBox.size());
+
+}
+
+
TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) {
int uid = 1111;
@@ -1796,6 +1830,53 @@ TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUidAttributionCha
EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value);
}
+TEST(StatsLogProcessorTest, TestDumpReportWithoutErasingDataDoesNotUpdateTimestamp) {
+ int hostUid = 20;
+ int isolatedUid = 30;
+ sp mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+ ConfigKey key(3, 4);
+
+ // TODO: All tests should not persist state on disk. This removes any reports that were present.
+ ProtoOutputStream proto;
+ StorageManager::appendConfigMetricsReport(key, &proto, /*erase data=*/true, /*isAdb=*/false);
+
+ StatsdConfig config = MakeConfig(false);
+ sp processor =
+ CreateStatsLogProcessor(1, 1, config, key, nullptr, 0, mockUidMap);
+ vector bytes;
+
+ int64_t dumpTime1Ns = 1 * NS_PER_SEC;
+ processor->onDumpReport(key, dumpTime1Ns, false /* include_current_bucket */,
+ true /* erase_data */, ADB_DUMP, FAST, &bytes);
+
+ ConfigMetricsReportList output;
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime1Ns);
+
+ int64_t dumpTime2Ns = 5 * NS_PER_SEC;
+ processor->onDumpReport(key, dumpTime2Ns, false /* include_current_bucket */,
+ false /* erase_data */, ADB_DUMP, FAST, &bytes);
+
+ // Check that the dump report without clearing data is successful.
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime2Ns);
+ EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns);
+
+ int64_t dumpTime3Ns = 10 * NS_PER_SEC;
+ processor->onDumpReport(key, dumpTime3Ns, false /* include_current_bucket */,
+ true /* erase_data */, ADB_DUMP, FAST, &bytes);
+
+ // Check that the previous dump report that didn't clear data did not overwrite the first dump's
+ // timestamps.
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime3Ns);
+ EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns);
+
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
index 322cfaf68a41a1088a5a94910a2b7b6f0c339f21..64ea219c84656d87ac1e8530b09b78a6b5239718 100644
--- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -43,23 +43,47 @@ TEST(AlarmTrackerTest, TestTriggerTimestamp) {
alarm.set_offset_millis(15 * MS_PER_SEC);
alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr
int64_t startMillis = 100000000 * MS_PER_SEC;
+ int64_t nextAlarmTime = startMillis / MS_PER_SEC + 15;
AlarmTracker tracker(startMillis, startMillis, alarm, kConfigKey, subscriberAlarmMonitor);
- EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15));
+ EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10;
std::unordered_set, SpHash> firedAlarmSet =
subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec));
EXPECT_TRUE(firedAlarmSet.empty());
tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
- EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15));
+ EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+ EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
currentTimeSec = startMillis / MS_PER_SEC + 7000;
+ nextAlarmTime = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60;
firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec));
ASSERT_EQ(firedAlarmSet.size(), 1u);
tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
EXPECT_TRUE(firedAlarmSet.empty());
- EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15 + 2 * 60 * 60));
+ EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+ EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
+
+ // Alarm fires exactly on time.
+ currentTimeSec = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60;
+ nextAlarmTime = startMillis / MS_PER_SEC + 15 + 3 * 60 * 60;
+ firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec));
+ ASSERT_EQ(firedAlarmSet.size(), 1u);
+ tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+ EXPECT_TRUE(firedAlarmSet.empty());
+ EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+ EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
+
+ // Alarm fires exactly 1 period late.
+ currentTimeSec = startMillis / MS_PER_SEC + 15 + 4 * 60 * 60;
+ nextAlarmTime = startMillis / MS_PER_SEC + 15 + 5 * 60 * 60;
+ firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast(currentTimeSec));
+ ASSERT_EQ(firedAlarmSet.size(), 1u);
+ tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+ EXPECT_TRUE(firedAlarmSet.empty());
+ EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+ EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
}
} // namespace statsd
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 5cc10cd9840ce996029f2e7cef55365e7211b3e8..428c46f8a0d2ad0f19e65f14b075f813774f65ec 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -306,6 +306,8 @@ TEST(StatsdStatsTest, TestPullAtomStats) {
stats.notePullUidProviderNotFound(util::DISK_SPACE);
stats.notePullerNotFound(util::DISK_SPACE);
stats.notePullerNotFound(util::DISK_SPACE);
+ stats.notePullTimeout(util::DISK_SPACE, 3000L, 6000L);
+ stats.notePullTimeout(util::DISK_SPACE, 4000L, 7000L);
vector output;
stats.dumpStats(&output, false);
@@ -328,6 +330,13 @@ TEST(StatsdStatsTest, TestPullAtomStats) {
EXPECT_EQ(1L, report.pulled_atom_stats(0).binder_call_failed());
EXPECT_EQ(1L, report.pulled_atom_stats(0).failed_uid_provider_not_found());
EXPECT_EQ(2L, report.pulled_atom_stats(0).puller_not_found());
+ ASSERT_EQ(2, report.pulled_atom_stats(0).pull_atom_metadata_size());
+ EXPECT_EQ(3000L, report.pulled_atom_stats(0).pull_atom_metadata(0).pull_timeout_uptime_millis());
+ EXPECT_EQ(4000L, report.pulled_atom_stats(0).pull_atom_metadata(1).pull_timeout_uptime_millis());
+ EXPECT_EQ(6000L, report.pulled_atom_stats(0).pull_atom_metadata(0)
+ .pull_timeout_elapsed_millis());
+ EXPECT_EQ(7000L, report.pulled_atom_stats(0).pull_atom_metadata(1)
+ .pull_timeout_elapsed_millis());
}
TEST(StatsdStatsTest, TestAtomMetricsStats) {
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 1bcc35d99d18f4a4f100f0eb0fe27c9ef19dde26..5666501d7d510fa50fd479e8d15e954e09f936b4 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -167,6 +167,32 @@ public:
return valueProducer;
}
+ static sp createValueProducerWithConditionAndState(
+ sp& pullerManager, ValueMetric& metric,
+ vector slicedStateAtoms,
+ unordered_map> stateGroupMap,
+ ConditionState conditionAfterFirstBucketPrepared) {
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp wizard = new NaggyMock();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
+ .WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _))
+ .WillRepeatedly(Return());
+
+ sp valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, 0 /* condition tracker index */, {ConditionState::kUnknown},
+ wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap);
+ valueProducer->prepareFirstBucket();
+ valueProducer->mCondition = conditionAfterFirstBucketPrepared;
+ return valueProducer;
+ }
+
static ValueMetric createMetric() {
ValueMetric metric;
metric.set_id(metricId);
@@ -188,6 +214,13 @@ public:
metric.add_slice_by_state(StringToId(state));
return metric;
}
+
+ static ValueMetric createMetricWithConditionAndState(string state) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_condition(StringToId("SCREEN_ON"));
+ metric.add_slice_by_state(StringToId(state));
+ return metric;
+ }
};
// Setup for parameterized tests.
@@ -3262,11 +3295,15 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWron
report.value_metrics().skipped(0).start_bucket_elapsed_millis());
EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+ ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size());
auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(1);
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), dropEvent.drop_time_millis());
}
/*
@@ -3582,7 +3619,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) {
sp valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithCondition(
- pullerManager, metric, ConditionState::kUnknown);
+ pullerManager, metric, ConditionState::kFalse);
// Check dump report.
ProtoOutputStream output;
@@ -3607,6 +3644,94 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) {
EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
}
+/*
+ * Test that all buckets are dropped due to condition unknown until the first onConditionChanged.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuckets) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp pullerManager = new StrictMock();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ // Condition change to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 10));
+ return true;
+ }))
+ // Dump report requested.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 15 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ sp valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(
+ pullerManager, metric, ConditionState::kUnknown);
+
+ // Bucket should be dropped because of condition unknown.
+ int64_t appUpgradeTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC;
+ valueProducer->notifyAppUpgrade(appUpgradeTimeNs);
+
+ // Bucket also dropped due to condition unknown
+ vector> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 3));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // This bucket is also dropped due to condition unknown.
+ int64_t conditionChangeTimeNs = bucket2StartTimeNs + 10 * NS_PER_SEC;
+ valueProducer->onConditionChanged(true, conditionChangeTimeNs);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set strSet;
+ int64_t dumpReportTimeNs = bucket2StartTimeNs + 15 * NS_PER_SEC; // 15 seconds
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true,
+ NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(0, report.value_metrics().data_size());
+ ASSERT_EQ(3, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+ auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), dropEvent.drop_time_millis());
+
+ EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
+ report.value_metrics().skipped(1).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ report.value_metrics().skipped(1).end_bucket_elapsed_millis());
+ ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size());
+
+ dropEvent = report.value_metrics().skipped(1).drop_event(0);
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
+
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ report.value_metrics().skipped(2).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
+ report.value_metrics().skipped(2).end_bucket_elapsed_millis());
+ ASSERT_EQ(1, report.value_metrics().skipped(2).drop_event_size());
+
+ dropEvent = report.value_metrics().skipped(2).drop_event(0);
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(conditionChangeTimeNs), dropEvent.drop_time_millis());
+}
+
/*
* Test that a skipped bucket is logged when a forced bucket split occurs when the previous bucket
* was not flushed in time.
@@ -3893,13 +4018,13 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
return true;
}));
+ StateManager::getInstance().clear();
sp valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithState(
pullerManager, metric, {util::SCREEN_STATE_CHANGED}, {});
EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
// Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().clear();
StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
@@ -4105,12 +4230,12 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
}
}
+ StateManager::getInstance().clear();
sp valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithState(
pullerManager, metric, {util::SCREEN_STATE_CHANGED}, stateGroupMap);
// Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().clear();
StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
@@ -4357,12 +4482,12 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
return true;
}));
+ StateManager::getInstance().clear();
sp valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithState(
pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {});
// Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().clear();
StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
@@ -4722,6 +4847,212 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long());
}
+TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
+ "BATTERY_SAVER_MODE_STATE");
+ sp pullerManager = new StrictMock();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ // Condition changed to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 3));
+ return true;
+ }))
+ // Battery saver mode state changed to OFF.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Condition changed to false.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
+ ConditionState::kFalse);
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after battery saver mode ON event.
+ // Condition is false so we do nothing.
+ unique_ptr batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+ EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ EXPECT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+
+ // Bucket status after condition change to true.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ std::unordered_map>::iterator
+ itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
+ EXPECT_TRUE(itBase->second[0].hasBase);
+ EXPECT_EQ(3, itBase->second[0].base.long_value);
+ EXPECT_TRUE(itBase->second[0].hasCurrentState);
+ ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, -1}
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ std::unordered_map>::iterator
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_FALSE(it->second[0].hasValue);
+
+ // Bucket status after battery saver mode OFF event.
+ unique_ptr batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
+ EXPECT_TRUE(itBase->second[0].hasBase);
+ EXPECT_EQ(5, itBase->second[0].base.long_value);
+ EXPECT_TRUE(itBase->second[0].hasCurrentState);
+ ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second[0].hasValue);
+ EXPECT_EQ(2, it->second[0].value.long_value);
+
+ // Pull at end of first bucket.
+ vector> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
+ EXPECT_TRUE(itBase->second[0].hasBase);
+ EXPECT_EQ(11, itBase->second[0].base.long_value);
+ EXPECT_TRUE(itBase->second[0].hasCurrentState);
+ ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ itBase->second[0].currentState.getValues()[0].mValue.int_value);
+
+ // Bucket 2 status after condition change to false.
+ valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
+ EXPECT_FALSE(itBase->second[0].hasBase);
+ EXPECT_TRUE(itBase->second[0].hasCurrentState);
+ ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ itBase->second[0].currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second[0].hasValue);
+ EXPECT_EQ(4, it->second[0].value.long_value);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(2, report.value_metrics().data_size());
+
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(2, data.bucket_info(0).values(0).value_long());
+
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(6, data.bucket_info(0).values(0).value_long());
+ EXPECT_EQ(4, data.bucket_info(1).values(0).value_long());
+}
+
+/*
+ * Test bucket splits when condition is unknown.
+ */
+TEST(ValueMetricProducerTest, TestForcedBucketSplitWhenConditionUnknownSkipsBucket) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp pullerManager = new StrictMock();
+
+ sp valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(
+ pullerManager, metric,
+ ConditionState::kUnknown);
+
+ // App update event.
+ int64_t appUpdateTimeNs = bucketStartTimeNs + 1000;
+ valueProducer->notifyAppUpgrade(appUpdateTimeNs);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds
+ valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true,
+ NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(0, report.value_metrics().data_size());
+ ASSERT_EQ(1, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(appUpdateTimeNs),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+ auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 06e29d4b5de172aed9734b484ae52788d56d0845..cee83725d075aa8387680a20b0bc69f1441e87b8 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -663,6 +663,8 @@ std::unique_ptr CreateBatterySaverOnEvent(uint64_t timestampNs) {
AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -674,6 +676,8 @@ std::unique_ptr CreateBatterySaverOffEvent(uint64_t timestampNs) {
AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr logEvent = std::make_unique(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
diff --git a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp
index db402a0dd658a2c2f630ac9089de97cb4bd60a53..32cecd3b9dbc393b31094b8e42df7a007f30880e 100644
--- a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp
+++ b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp
@@ -50,13 +50,13 @@ TEST(MultiConditionTrigger, TestMultipleConditions) {
});
vector threads;
- vector done(numConditions, false);
+ vector done(numConditions, 0);
int i = 0;
for (const string& conditionName : conditionNames) {
threads.emplace_back([&done, &conditionName, &trigger, i] {
sleep_for(chrono::milliseconds(3));
- done[i] = true;
+ done[i] = 1;
trigger.markComplete(conditionName);
});
i++;
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index 6384fb12ca683b63063f999ce5a9c598c8143215..51bcad115cc54deee4bea1187749480cb5e8d728 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -342,6 +342,9 @@ public class TestDrive {
.addPullAtomPackages(PullAtomPackages.newBuilder()
.setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER)
.addPackages("AID_STATSD"))
+ .addPullAtomPackages(PullAtomPackages.newBuilder()
+ .setAtomId(Atom.GENERAL_EXTERNAL_STORAGE_ACCESS_STATS_FIELD_NUMBER)
+ .addPackages("com.google.android.providers.media.module"))
.setHashStringsInMetricReport(false);
}
}
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index fe270a480d83f4d3a40a4f2e782c8cf3e99ecc37..fed9c43faa38ea90d1886e6f69f2fc4d37add643 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -68,6 +68,8 @@ public final class Telecom extends BaseCommand {
private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
+ private static final String COMMAND_STOP_BLOCK_SUPPRESSION = "stop-block-suppression";
+
/**
* Change the system dialer package name if a package name was specified,
* Example: adb shell telecom set-system-dialer
@@ -115,6 +117,8 @@ public final class Telecom extends BaseCommand {
+ "usage: telecom set-sim-count \n"
+ "usage: telecom get-sim-config\n"
+ "usage: telecom get-max-phones\n"
+ + "usage: telecom stop-block-suppression: Stop suppressing the blocked number"
+ + " provider after a call to emergency services.\n"
+ "usage: telecom set-emer-phone-account-filter \n"
+ "\n"
+ "telecom set-phone-account-enabled: Enables the given phone account, if it has"
@@ -207,6 +211,9 @@ public final class Telecom extends BaseCommand {
case COMMAND_UNREGISTER_PHONE_ACCOUNT:
runUnregisterPhoneAccount();
break;
+ case COMMAND_STOP_BLOCK_SUPPRESSION:
+ runStopBlockSuppression();
+ break;
case COMMAND_SET_DEFAULT_DIALER:
runSetDefaultDialer();
break;
@@ -324,8 +331,13 @@ public final class Telecom extends BaseCommand {
System.out.println("Success - " + handle + " unregistered.");
}
+ private void runStopBlockSuppression() throws RemoteException {
+ mTelecomService.stopBlockSuppression();
+ }
+
private void runSetDefaultDialer() throws RemoteException {
- final String packageName = nextArgRequired();
+ String packageName = nextArg();
+ if ("default".equals(packageName)) packageName = null;
mTelecomService.setTestDefaultDialer(packageName);
System.out.println("Success - " + packageName + " set as override default dialer.");
}
diff --git a/config/preloaded-classes-blacklist b/config/preloaded-classes-blacklist
index 353f786da1533d8bfdd15bc810d3c7f72017d330..48d579cc118e60b60a74ab0e44cecf868da861dc 100644
--- a/config/preloaded-classes-blacklist
+++ b/config/preloaded-classes-blacklist
@@ -1,6 +1,8 @@
android.content.AsyncTaskLoader$LoadTask
android.net.ConnectivityThread$Singleton
+android.os.AsyncTask
android.os.FileObserver
+android.os.NullVibrator
android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
android.widget.Magnifier
-sun.nio.fs.UnixChannelFactory
+com.android.server.BootReceiver$2
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index ed0ea556dc9d622ef45f7d3ca1d90b76c7250141..ac00a042b79e662a6196e556fac44738991a001d 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -520,6 +520,12 @@ public abstract class AccessibilityService extends Service {
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13;
+ /**
+ * Action to show Launcher's all apps.
+ * @hide
+ */
+ public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14;
+
private static final String LOG_TAG = "AccessibilityService";
/**
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 87fc8fe392f01468c618e44dbf0ca346f06d37bd..3772755beca18e8cb4e9f360ae2ab08bd69172b8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2816,6 +2816,11 @@ public class Activity extends ContextThemeWrapper
* The system may disallow entering picture-in-picture in various cases, including when the
* activity is not visible, if the screen is locked or if the user has an activity pinned.
*
+ *
By default, system calculates the dimension of picture-in-picture window based on the
+ * given {@param params}.
+ * See Picture-in-picture Support
+ * on how to override this behavior.
+ *
* @see android.R.attr#supportsPictureInPicture
* @see PictureInPictureParams
*
@@ -5151,6 +5156,10 @@ public class Activity extends ContextThemeWrapper
* the signature of the app declaring the permissions.
*
*
+ * Call {@link #shouldShowRequestPermissionRationale(String)} before calling this API to
+ * check if the system recommends to show a rationale UI before asking for a permission.
+ *
+ *
* If your app does not have the requested permissions the user will be presented
* with UI for accepting them. After the user has accepted or rejected the
* requested permissions you will receive a callback on {@link
@@ -5240,20 +5249,10 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Gets whether you should show UI with rationale for requesting a permission.
- * You should do this only if you do not have the permission and the context in
- * which the permission is requested does not clearly communicate to the user
- * what would be the benefit from granting this permission.
- *
- * For example, if you write a camera app, requesting the camera permission
- * would be expected by the user and no rationale for why it is requested is
- * needed. If however, the app needs location for tagging photos then a non-tech
- * savvy user may wonder how location is related to taking photos. In this case
- * you may choose to show UI with rationale of requesting this permission.
- *
+ * Gets whether you should show UI with rationale before requesting a permission.
*
* @param permission A permission your app wants to request.
- * @return Whether you can show permission rationale UI.
+ * @return Whether you should show permission rationale UI.
*
* @see #checkSelfPermission(String)
* @see #requestPermissions(String[], int)
@@ -5568,7 +5567,7 @@ public class Activity extends ContextThemeWrapper
options = transferSpringboardActivityOptions(options);
String resolvedType = null;
if (fillInIntent != null) {
- fillInIntent.migrateExtraStreamToClipData();
+ fillInIntent.migrateExtraStreamToClipData(this);
fillInIntent.prepareToLeaveProcess(this);
resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
}
@@ -5823,7 +5822,7 @@ public class Activity extends ContextThemeWrapper
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
- intent.migrateExtraStreamToClipData();
+ intent.migrateExtraStreamToClipData(this);
intent.prepareToLeaveProcess(this);
result = ActivityTaskManager.getService()
.startActivity(mMainThread.getApplicationThread(), getBasePackageName(),
@@ -5894,7 +5893,7 @@ public class Activity extends ContextThemeWrapper
@Nullable Bundle options) {
if (mParent == null) {
try {
- intent.migrateExtraStreamToClipData();
+ intent.migrateExtraStreamToClipData(this);
intent.prepareToLeaveProcess(this);
return ActivityTaskManager.getService()
.startNextMatchingActivity(mToken, intent, options);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 97b704ccc1c9b8d1c229fbddd873fafeeab5a0ec..acf6315ddc5df736b4b275124566b7763f316535 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -159,10 +159,10 @@ public class ActivityManager {
*/
public static final int INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
/**
- * Mount full external storage for the newly started instrumentation.
+ * Grant full access to the external storage for the newly started instrumentation.
* @hide
*/
- public static final int INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL = 1 << 1;
+ public static final int INSTR_FLAG_DISABLE_ISOLATED_STORAGE = 1 << 1;
/**
* Disable test API access for the newly started instrumentation.
@@ -601,20 +601,6 @@ public class ActivityManager {
@TestApi
public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 2;
- // TODO: remove this when development is done.
- // These are debug flags used between OomAdjuster and AppOpsService to detect and report absence
- // of the real flags.
- /** @hide */
- public static final int DEBUG_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE_Q = 1 << 27;
- /** @hide */
- public static final int DEBUG_PROCESS_CAPABILITY_FOREGROUND_CAMERA_Q = 1 << 28;
- /** @hide */
- public static final int DEBUG_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 29;
- /** @hide */
- public static final int DEBUG_PROCESS_CAPABILITY_FOREGROUND_CAMERA = 1 << 30;
- /** @hide */
- public static final int DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
-
/** @hide all capabilities, the ORing of all flags in {@link ProcessCapability}*/
@TestApi
public static final int PROCESS_CAPABILITY_ALL = PROCESS_CAPABILITY_FOREGROUND_LOCATION
@@ -653,29 +639,9 @@ public class ActivityManager {
*/
public static void printCapabilitiesFull(PrintWriter pw, @ProcessCapability int caps) {
printCapabilitiesSummary(pw, caps);
- if ((caps & DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0) {
- pw.print(" !L");
- }
- if ((caps & DEBUG_PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
- pw.print(" !C");
- }
- if ((caps & DEBUG_PROCESS_CAPABILITY_FOREGROUND_CAMERA_Q) != 0) {
- pw.print(" !Cq");
- }
- if ((caps & DEBUG_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
- pw.print(" !M");
- }
- if ((caps & DEBUG_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE_Q) != 0) {
- pw.print(" !Mq");
- }
final int remain = caps & ~(PROCESS_CAPABILITY_FOREGROUND_LOCATION
| PROCESS_CAPABILITY_FOREGROUND_CAMERA
- | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE
- | DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION
- | DEBUG_PROCESS_CAPABILITY_FOREGROUND_CAMERA
- | DEBUG_PROCESS_CAPABILITY_FOREGROUND_CAMERA_Q
- | DEBUG_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE
- | DEBUG_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE_Q);
+ | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE);
if (remain != 0) {
pw.print('+');
pw.print(remain);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index c491eea7a674ac399205d157dd0abc589f8c7158..a5965bc7f85fd6f538b0bca9053aedabe25e079c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -398,13 +398,6 @@ public abstract class ActivityManagerInternal {
*/
public abstract boolean isUidCurrentlyInstrumented(int uid);
- /**
- * Show a debug toast, asking user to file a bugreport.
- */
- // TODO: remove this toast after feature development is done
- public abstract void showWhileInUseDebugToast(int uid, int op, int mode);
-
-
/** Is this a device owner app? */
public abstract boolean isDeviceOwner(int uid);
@@ -429,11 +422,17 @@ public abstract class ActivityManagerInternal {
int userId, int[] appIdWhitelist);
/**
- * Add or delete uid from the ActivityManagerService PendingStartActivityUids list.
+ * Add uid to the ActivityManagerService PendingStartActivityUids list.
+ * @param uid uid
+ * @param pid pid of the ProcessRecord that is pending top.
+ */
+ public abstract void addPendingTopUid(int uid, int pid);
+
+ /**
+ * Delete uid from the ActivityManagerService PendingStartActivityUids list.
* @param uid uid
- * @param pending add to the list if true, delete from list if false.
*/
- public abstract void updatePendingTopUid(int uid, boolean pending);
+ public abstract void deletePendingTopUid(int uid);
/**
* Is the uid in ActivityManagerService PendingStartActivityUids list?
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 1cc63da3db0a62e333843472ac9411526946aae2..0f31529451fb3466fdb88776521fbb9671de213a 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -433,13 +433,21 @@ public class ActivityTaskManager {
}
}
- /** Returns whether the current UI mode supports error dialogs (ANR, crash, etc). */
- public static boolean currentUiModeSupportsErrorDialogs(@NonNull Context context) {
- final Configuration config = context.getResources().getConfiguration();
+ /**
+ * @return whether the UI mode of the given config supports error dialogs (ANR, crash, etc).
+ * @hide
+ */
+ public static boolean currentUiModeSupportsErrorDialogs(@NonNull Configuration config) {
int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
return (modeType != Configuration.UI_MODE_TYPE_CAR
&& !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
&& modeType != Configuration.UI_MODE_TYPE_TELEVISION
&& modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
}
+
+ /** @return whether the current UI mode supports error dialogs (ANR, crash, etc). */
+ public static boolean currentUiModeSupportsErrorDialogs(@NonNull Context context) {
+ final Configuration config = context.getResources().getConfiguration();
+ return currentUiModeSupportsErrorDialogs(config);
+ }
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cffa59c06a538f2a1ed6ee76a328453325295d01..812ca4aefb9b01fb9c55a80a410ea8a04a5335d4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -123,6 +123,7 @@ import android.os.SystemProperties;
import android.os.TelephonyServiceManager;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.UserManager;
import android.permission.IPermissionManager;
import android.provider.BlockedNumberContract;
import android.provider.CalendarContract;
@@ -3252,18 +3253,56 @@ public final class ActivityThread extends ClientTransactionHandler {
@Override
public void handleFixedRotationAdjustments(@NonNull IBinder token,
@Nullable FixedRotationAdjustments fixedRotationAdjustments) {
- final Consumer override = fixedRotationAdjustments != null
- ? displayAdjustments -> displayAdjustments.setFixedRotationAdjustments(
- fixedRotationAdjustments)
- : null;
+ handleFixedRotationAdjustments(token, fixedRotationAdjustments, null /* overrideConfig */);
+ }
+
+ /**
+ * Applies the rotation adjustments to override display information in resources belong to the
+ * provided token. If the token is activity token, the adjustments also apply to application
+ * because the appearance of activity is usually more sensitive to the application resources.
+ *
+ * @param token The token to apply the adjustments.
+ * @param fixedRotationAdjustments The information to override the display adjustments of
+ * corresponding resources. If it is null, the exiting override
+ * will be cleared.
+ * @param overrideConfig The override configuration of activity. It is used to override
+ * application configuration. If it is non-null, it means the token is
+ * confirmed as activity token. Especially when launching new activity,
+ * {@link #mActivities} hasn't put the new token.
+ */
+ private void handleFixedRotationAdjustments(@NonNull IBinder token,
+ @Nullable FixedRotationAdjustments fixedRotationAdjustments,
+ @Nullable Configuration overrideConfig) {
+ // The element of application configuration override is set only if the application
+ // adjustments are needed, because activity already has its own override configuration.
+ final Configuration[] appConfigOverride;
+ final Consumer override;
+ if (fixedRotationAdjustments != null) {
+ appConfigOverride = new Configuration[1];
+ override = displayAdjustments -> {
+ displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ if (appConfigOverride[0] != null) {
+ displayAdjustments.getConfiguration().updateFrom(appConfigOverride[0]);
+ }
+ };
+ } else {
+ appConfigOverride = null;
+ override = null;
+ }
if (!mResourcesManager.overrideTokenDisplayAdjustments(token, override)) {
// No resources are associated with the token.
return;
}
- if (mActivities.get(token) == null) {
- // Only apply the override to application for activity token because the appearance of
- // activity is usually more sensitive to the application resources.
- return;
+ if (overrideConfig == null) {
+ final ActivityClientRecord r = mActivities.get(token);
+ if (r == null) {
+ // It is not an activity token. Nothing to do for application.
+ return;
+ }
+ overrideConfig = r.overrideConfig;
+ }
+ if (appConfigOverride != null) {
+ appConfigOverride[0] = overrideConfig;
}
// Apply the last override to application resources for compatibility. Because the Resources
@@ -3503,7 +3542,8 @@ public final class ActivityThread extends ClientTransactionHandler {
// The rotation adjustments must be applied before creating the activity, so the activity
// can get the adjusted display info during creation.
if (r.mPendingFixedRotationAdjustments != null) {
- handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments);
+ handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments,
+ r.overrideConfig);
r.mPendingFixedRotationAdjustments = null;
}
@@ -6777,7 +6817,11 @@ public final class ActivityThread extends ClientTransactionHandler {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
- Slog.e(TAG, "Failed to find provider info for " + auth);
+ if (UserManager.get(c).isUserUnlocked(userId)) {
+ Slog.e(TAG, "Failed to find provider info for " + auth);
+ } else {
+ Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)");
+ }
return null;
}
@@ -7388,6 +7432,10 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
+ public Bundle getCoreSettings() {
+ return mCoreSettings;
+ }
+
public int getIntCoreSetting(String key, int defaultValue) {
synchronized (mResourcesManager) {
if (mCoreSettings != null) {
@@ -7397,6 +7445,18 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
+ /**
+ * Get the string value of the given key from core settings.
+ */
+ public String getStringCoreSetting(String key, String defaultValue) {
+ synchronized (mResourcesManager) {
+ if (mCoreSettings != null) {
+ return mCoreSettings.getString(key, defaultValue);
+ }
+ return defaultValue;
+ }
+ }
+
float getFloatCoreSetting(String key, float defaultValue) {
synchronized (mResourcesManager) {
if (mCoreSettings != null) {
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 635ed138cc65ece9dfbf0af5556f3f12ea273f50..98a23f2b007502ce78dbac5bc714bd96c4993574 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -33,7 +33,10 @@ import android.hardware.display.VirtualDisplay;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.KeyEvent;
@@ -102,6 +105,14 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
public ActivityView(
@NonNull Context context, @NonNull AttributeSet attrs, int defStyle,
boolean singleTaskInstance, boolean usePublicVirtualDisplay) {
+ this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay, false);
+ }
+
+ /** @hide */
+ public ActivityView(
+ @NonNull Context context, @NonNull AttributeSet attrs, int defStyle,
+ boolean singleTaskInstance, boolean usePublicVirtualDisplay,
+ boolean disableSurfaceViewBackgroundLayer) {
super(context, attrs, defStyle);
if (useTaskOrganizer()) {
mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this);
@@ -109,7 +120,7 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
mTaskEmbedder = new VirtualDisplayTaskEmbedder(context, this, singleTaskInstance,
usePublicVirtualDisplay);
}
- mSurfaceView = new SurfaceView(context);
+ mSurfaceView = new SurfaceView(context, null, 0, 0, disableSurfaceViewBackgroundLayer);
// Since ActivityView#getAlpha has been overridden, we should use parent class's alpha
// as master to synchronize surface view's alpha value.
mSurfaceView.setAlpha(super.getAlpha());
@@ -352,18 +363,9 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
}
/**
- * Release this container. Activity launching will no longer be permitted.
- *
Note: Calling this method is allowed after
- * {@link StateCallback#onActivityViewReady(ActivityView)} callback was triggered and before
- * {@link StateCallback#onActivityViewDestroyed(ActivityView)}.
- *
- * @see StateCallback
+ * Release this container if it is initialized. Activity launching will no longer be permitted.
*/
public void release() {
- if (!mTaskEmbedder.isInitialized()) {
- throw new IllegalStateException(
- "Trying to release container that is not initialized.");
- }
performRelease();
}
@@ -408,6 +410,9 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
}
private class SurfaceCallback implements SurfaceHolder.Callback {
+ private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
+ private final DisplayMetrics mTempMetrics = new DisplayMetrics();
+
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
if (!mTaskEmbedder.isInitialized()) {
@@ -416,13 +421,21 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
mTmpTransaction.reparent(mTaskEmbedder.getSurfaceControl(),
mSurfaceView.getSurfaceControl()).apply();
}
+ mTaskEmbedder.resizeTask(getWidth(), getHeight());
mTaskEmbedder.start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
- mTaskEmbedder.resizeTask(width, height);
- mTaskEmbedder.notifyBoundsChanged();
+ final Display display = getVirtualDisplay().getDisplay();
+ if (!display.getDisplayInfo(mTempDisplayInfo)) {
+ return;
+ }
+ mTempDisplayInfo.getAppMetrics(mTempMetrics);
+ if (width != mTempMetrics.widthPixels || height != mTempMetrics.heightPixels) {
+ mTaskEmbedder.resizeTask(width, height);
+ mTaskEmbedder.notifyBoundsChanged();
+ }
}
@Override
@@ -479,7 +492,9 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
return;
}
mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
- mTaskEmbedder.release();
+ if (mTaskEmbedder.isInitialized()) {
+ mTaskEmbedder.release();
+ }
mTaskEmbedder.setListener(null);
mGuard.close();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index b058dcd714dd68f72ac41513c713eab7cc72b78f..0a6827cde3d351e49812e78dff6b3029aecc0074 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -182,6 +182,8 @@ public class AppOpsManager {
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
public static final long CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE = 148180766L;
+ private static final int MAX_UNFORWARDED_OPS = 10;
+
final Context mContext;
@UnsupportedAppUsage
@@ -216,6 +218,17 @@ public class AppOpsManager {
@GuardedBy("sLock")
private static @Nullable OnOpNotedCallback sOnOpNotedCallback;
+ /**
+ * Sync note-ops collected from {@link #readAndLogNotedAppops(Parcel)} that have not been
+ * delivered to a callback yet.
+ *
+ * Similar to {@link com.android.server.appop.AppOpsService#mUnforwardedAsyncNotedOps} for
+ * {@link COLLECT_ASYNC}. Used in situation when AppOpsManager asks to collect stacktrace with
+ * {@link #sMessageCollector}, which forces {@link COLLECT_SYNC} mode.
+ */
+ @GuardedBy("sLock")
+ private static ArrayList sUnforwardedOps = new ArrayList<>();
+
/**
* Additional collector that collect accesses and forwards a few of them them via
* {@link IAppOpsService#reportRuntimeAppOpAccessMessageAndGetConfig}.
@@ -248,8 +261,9 @@ public class AppOpsManager {
< SystemClock.elapsedRealtime()) {
String stackTrace = getFormattedStackTrace();
try {
+ String packageName = ActivityThread.currentOpPackageName();
sConfig = getService().reportRuntimeAppOpAccessMessageAndGetConfig(
- ActivityThread.currentOpPackageName(), op, stackTrace);
+ packageName == null ? "" : packageName, op, stackTrace);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -1107,9 +1121,12 @@ public class AppOpsManager {
public static final int OP_AUTO_REVOKE_MANAGED_BY_INSTALLER =
AppProtoEnums.APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER;
+ /** @hide */
+ public static final int OP_NO_ISOLATED_STORAGE = AppProtoEnums.APP_OP_NO_ISOLATED_STORAGE;
+
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 99;
+ public static final int _NUM_OP = 100;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1420,6 +1437,12 @@ public class AppOpsManager {
@SystemApi
public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats";
+ /**
+ * AppOp granted to apps that we are started via {@code am instrument -e --no-isolated-storage}
+ *
+ * @hide
+ */
+ public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
@@ -1609,6 +1632,7 @@ public class AppOpsManager {
OP_DEPRECATED_1, // deprecated
OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED
OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
+ OP_NO_ISOLATED_STORAGE, // NO_ISOLATED_STORAGE
};
/**
@@ -1714,6 +1738,7 @@ public class AppOpsManager {
"", // deprecated
OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER,
+ OPSTR_NO_ISOLATED_STORAGE,
};
/**
@@ -1820,6 +1845,7 @@ public class AppOpsManager {
"deprecated",
"AUTO_REVOKE_PERMISSIONS_IF_UNUSED",
"AUTO_REVOKE_MANAGED_BY_INSTALLER",
+ "NO_ISOLATED_STORAGE",
};
/**
@@ -1927,6 +1953,7 @@ public class AppOpsManager {
null, // deprecated operation
null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
null, // no permission for OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
+ null, // no permission for OP_NO_ISOLATED_STORAGE
};
/**
@@ -2034,6 +2061,7 @@ public class AppOpsManager {
null, // deprecated operation
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
+ null, // NO_ISOLATED_STORAGE
};
/**
@@ -2140,6 +2168,7 @@ public class AppOpsManager {
null, // deprecated operation
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
+ null, // NO_ISOLATED_STORAGE
};
/**
@@ -2245,6 +2274,7 @@ public class AppOpsManager {
AppOpsManager.MODE_IGNORED, // deprecated operation
AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
+ AppOpsManager.MODE_ERRORED, // OP_NO_ISOLATED_STORAGE
};
/**
@@ -2354,6 +2384,7 @@ public class AppOpsManager {
false, // deprecated operation
false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
false, // AUTO_REVOKE_MANAGED_BY_INSTALLER
+ true, // NO_ISOLATED_STORAGE
};
/**
@@ -7339,15 +7370,17 @@ public class AppOpsManager {
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
+ boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID ? true : false;
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
message = getFormattedStackTrace();
+ shouldCollectMessage = true;
}
}
int mode = mService.noteOperation(op, uid, packageName, attributionTag,
- collectionMode == COLLECT_ASYNC, message);
+ collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
@@ -7500,16 +7533,19 @@ public class AppOpsManager {
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(proxiedUid, proxiedPackageName, op);
+ boolean shouldCollectMessage = myUid == Process.SYSTEM_UID ? true : false;
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
message = getFormattedStackTrace();
+ shouldCollectMessage = true;
}
}
int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
proxiedAttributionTag, myUid, mContext.getOpPackageName(),
- mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message);
+ mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
@@ -7824,15 +7860,18 @@ public class AppOpsManager {
try {
collectNoteOpCallsForValidation(op);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
+ boolean shouldCollectMessage = Process.myUid() == Process.SYSTEM_UID ? true : false;
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
message = getFormattedStackTrace();
+ shouldCollectMessage = true;
}
}
int mode = mService.startOperation(getClientId(), op, uid, packageName,
- attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message);
+ attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
@@ -8163,6 +8202,14 @@ public class AppOpsManager {
code = notedAppOps.nextSetBit(code + 1)) {
if (sOnOpNotedCallback != null) {
sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag));
+ } else {
+ String message = getFormattedStackTrace();
+ sUnforwardedOps.add(
+ new AsyncNotedAppOp(code, Process.myUid(), attributionTag,
+ message, System.currentTimeMillis()));
+ if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) {
+ sUnforwardedOps.remove(0);
+ }
}
}
}
@@ -8229,6 +8276,17 @@ public class AppOpsManager {
}
}
}
+ synchronized (this) {
+ int numMissedSyncOps = sUnforwardedOps.size();
+ for (int i = 0; i < numMissedSyncOps; i++) {
+ final AsyncNotedAppOp syncNotedAppOp = sUnforwardedOps.get(i);
+ if (sOnOpNotedCallback != null) {
+ sOnOpNotedCallback.getAsyncNotedExecutor().execute(
+ () -> sOnOpNotedCallback.onAsyncNoted(syncNotedAppOp));
+ }
+ }
+ sUnforwardedOps.clear();
+ }
}
}
}
@@ -8576,6 +8634,25 @@ public class AppOpsManager {
}
}
+ /**
+ * Reboots the ops history.
+ *
+ * @param offlineDurationMillis The duration to wait between
+ * tearing down and initializing the history. Must be greater
+ * than or equal to zero.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_APPOPS)
+ public void rebootHistory(long offlineDurationMillis) {
+ try {
+ mService.rebootHistory(offlineDurationMillis);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Pulls current AppOps access report and picks package and op to watch for next access report
* Returns null if no reports were collected since last call. There is no guarantee of report
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 309e91f1e4ffca19e2f1fcc11318cc8c63d90a8d..5e032f00a3a02439ea07ea90f79fe48862dd3154 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -22,7 +22,7 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.app.IAppOpsCallback;
-import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.QuadFunction;
/**
@@ -73,9 +73,9 @@ public abstract class AppOpsManagerInternal {
*/
int noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String featureId, boolean shouldCollectAsyncNotedOp,
- @Nullable String message,
- @NonNull HexFunction
- superImpl);
+ @Nullable String message, boolean shouldCollectMessage,
+ @NonNull HeptFunction superImpl);
}
/**
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index cfe0aff05d4ab5bd251dccb6bf628bc952fc0386..e7b3e14bfda72e9c8efe6287af806c065f079200 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -502,7 +502,7 @@ public final class ApplicationExitInfo implements Parcelable {
* Return the defining kernel user identifier, maybe different from {@link #getRealUid} and
* {@link #getPackageUid}, if an external service has the
* {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set
- * to true and was bound with the flag
+ * to true and was bound with the flag
* {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here will
* be the kernel user identifier of the external service provider.
*/
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6bd8fd7c6ecfbdfa8308da49b1c9919c1225648d..c9031b711657b3adeeefef078b2ec3336340def6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -139,6 +139,10 @@ public class ApplicationPackageManager extends PackageManager {
public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS =
"app_permission_button_allow_always";
+ // Name of the package which the permission controller's resources are in.
+ public static final String PERMISSION_CONTROLLER_RESOURCE_PACKAGE =
+ "com.android.permissioncontroller";
+
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -759,25 +763,26 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public void revokeRuntimePermission(String packageName, String permName, UserHandle user) {
+ revokeRuntimePermission(packageName, permName, user, null);
+ }
+
+ @Override
+ public void revokeRuntimePermission(String packageName, String permName, UserHandle user,
+ String reason) {
if (DEBUG_TRACE_PERMISSION_UPDATES
&& shouldTraceGrant(packageName, permName, user.getIdentifier())) {
Log.i(TAG, "App " + mContext.getPackageName() + " is revoking " + packageName + " "
- + permName + " for user " + user.getIdentifier(), new RuntimeException());
+ + permName + " for user " + user.getIdentifier() + " with reason " + reason,
+ new RuntimeException());
}
try {
mPermissionManager
- .revokeRuntimePermission(packageName, permName, user.getIdentifier());
+ .revokeRuntimePermission(packageName, permName, user.getIdentifier(), reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- @Override
- public void revokeRuntimePermission(String packageName, String permName, UserHandle user,
- String reason) {
- // TODO evanseverson: impl
- }
-
@Override
public int getPermissionFlags(String permName, String packageName, UserHandle user) {
try {
@@ -893,8 +898,7 @@ public class ApplicationPackageManager extends PackageManager {
mContext.createPackageContext(permissionController, 0);
int textId = context.getResources().getIdentifier(APP_PERMISSION_BUTTON_ALLOW_ALWAYS,
- "string", "com.android.permissioncontroller");
-// permissionController); STOPSHIP b/147434671
+ "string", PERMISSION_CONTROLLER_RESOURCE_PACKAGE);
if (textId != 0) {
return context.getText(textId);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 48160e475d504fd329a43984fbabe5f08d1de4e8..505b498e3cf6fdfb65c9e74d11a36cb894cb97d5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1087,7 +1087,7 @@ class ContextImpl extends Context {
try {
String resolvedType = null;
if (fillInIntent != null) {
- fillInIntent.migrateExtraStreamToClipData();
+ fillInIntent.migrateExtraStreamToClipData(this);
fillInIntent.prepareToLeaveProcess(this);
resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
}
@@ -1900,24 +1900,31 @@ class ContextImpl extends Context {
@Override
public Object getSystemService(String name) {
- // Check incorrect Context usage.
- if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) {
- final String errorMessage = "Tried to access visual service "
- + SystemServiceRegistry.getSystemServiceClassName(name)
- + " from a non-visual Context. ";
- final String message = "Visual services, such as WindowManager, WallpaperService or "
- + "LayoutInflater should be accessed from Activity or other visual Context. "
- + "Use an Activity or a Context created with "
- + "Context#createWindowContext(int, Bundle), which are adjusted to the "
- + "configuration and visual bounds of an area on screen.";
- final Exception exception = new IllegalAccessException(errorMessage);
- StrictMode.onIncorrectContextUsed(message, exception);
- Log.e(TAG, errorMessage + message, exception);
+ if (vmIncorrectContextUseEnabled()) {
+ // We may override this API from outer context.
+ final boolean isUiContext = isUiContext() || isOuterUiContext();
+ // Check incorrect Context usage.
+ if (isUiComponent(name) && !isUiContext) {
+ final String errorMessage = "Tried to access visual service "
+ + SystemServiceRegistry.getSystemServiceClassName(name)
+ + " from a non-visual Context:" + getOuterContext();
+ final String message = "Visual services, such as WindowManager, WallpaperService "
+ + "or LayoutInflater should be accessed from Activity or other visual "
+ + "Context. Use an Activity or a Context created with "
+ + "Context#createWindowContext(int, Bundle), which are adjusted to "
+ + "the configuration and visual bounds of an area on screen.";
+ final Exception exception = new IllegalAccessException(errorMessage);
+ StrictMode.onIncorrectContextUsed(message, exception);
+ Log.e(TAG, errorMessage + " " + message, exception);
+ }
}
-
return SystemServiceRegistry.getSystemService(this, name);
}
+ private boolean isOuterUiContext() {
+ return getOuterContext() != null && getOuterContext().isUiContext();
+ }
+
@Override
public String getSystemServiceName(Class> serviceClass) {
return SystemServiceRegistry.getSystemServiceName(serviceClass);
@@ -2369,6 +2376,7 @@ class ContextImpl extends Context {
context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
mResources.getLoaders()));
+ context.mIsUiContext = isUiContext() || isOuterUiContext();
return context;
}
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 1278ff6817fd6131947b22357328a8dfc85171f3..0719422632d1d89a946a213c47c3a3b5945a953c 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -25,6 +25,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ClipDescription;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -50,11 +51,13 @@ import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.LongSparseArray;
import android.util.Pair;
+import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* The download manager is a system service that handles long-running HTTP downloads. Clients may
@@ -1554,6 +1557,7 @@ public class DownloadManager {
values.put(Downloads.Impl.COLUMN_DESTINATION,
Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD);
values.put(Downloads.Impl._DATA, path);
+ values.put(Downloads.Impl.COLUMN_MIME_TYPE, resolveMimeType(new File(path)));
values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_SUCCESS);
values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, length);
values.put(Downloads.Impl.COLUMN_MEDIA_SCANNED,
@@ -1569,6 +1573,58 @@ public class DownloadManager {
return Long.parseLong(downloadUri.getLastPathSegment());
}
+ /**
+ * Shamelessly borrowed from
+ * {@code packages/providers/MediaProvider/src/com/android/providers/media/util/MimeUtils.java}
+ *
+ * @hide
+ */
+ private static @NonNull String resolveMimeType(@NonNull File file) {
+ final String extension = extractFileExtension(file.getPath());
+ if (extension == null) return ClipDescription.MIMETYPE_UNKNOWN;
+
+ final String mimeType = MimeTypeMap.getSingleton()
+ .getMimeTypeFromExtension(extension.toLowerCase(Locale.ROOT));
+ if (mimeType == null) return ClipDescription.MIMETYPE_UNKNOWN;
+
+ return mimeType;
+ }
+
+ /**
+ * Shamelessly borrowed from
+ * {@code packages/providers/MediaProvider/src/com/android/providers/media/util/FileUtils.java}
+ *
+ * @hide
+ */
+ private static @Nullable String extractDisplayName(@Nullable String data) {
+ if (data == null) return null;
+ if (data.indexOf('/') == -1) {
+ return data;
+ }
+ if (data.endsWith("/")) {
+ data = data.substring(0, data.length() - 1);
+ }
+ return data.substring(data.lastIndexOf('/') + 1);
+ }
+
+ /**
+ * Shamelessly borrowed from
+ * {@code packages/providers/MediaProvider/src/com/android/providers/media/util/FileUtils.java}
+ *
+ * @hide
+ */
+ private static @Nullable String extractFileExtension(@Nullable String data) {
+ if (data == null) return null;
+ data = extractDisplayName(data);
+
+ final int lastDot = data.lastIndexOf('.');
+ if (lastDot == -1) {
+ return null;
+ } else {
+ return data.substring(lastDot + 1);
+ }
+ }
+
private static final String NON_DOWNLOADMANAGER_DOWNLOAD =
"non-dwnldmngr-download-dont-retry2download";
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index fd3eb0618429cce66cc1fe8a609507e6f870b26c..68824cd26eaaf061a9485cad3d9f14e6bbfffd68 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -434,8 +434,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
mSharedElementNotified = true;
delayCancel();
- if (!mActivity.isTopOfTask() || (mIsReturning && !mActivity.isTaskRoot()
- && !mSharedElements.isEmpty())) {
+ if (!mActivity.isTopOfTask()) {
mResultReceiver.send(MSG_ALLOW_RETURN_TRANSITION, null);
}
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index c6a0de458df0ee88a68f48ff4a7f44c24eb0bed9..e4e5ba37ddbf979bff73cbd44081e203ba3e9b2b 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1179,6 +1179,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* the signature of the app declaring the permissions.
*
*
+ * Call {@link #shouldShowRequestPermissionRationale(String)} before calling this API
+ * to check if the system recommends to show a rationale UI before asking for a permission.
+ *
+ *
* If your app does not have the requested permissions the user will be presented
* with UI for accepting them. After the user has accepted or rejected the
* requested permissions you will receive a callback on {@link
@@ -1213,29 +1217,6 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* true because in this case the activity would not receive
* result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}.
*
- *
- * A sample permissions request looks like this:
- *
*
* @param permissions The requested permissions. Must me non-null and not empty.
* @param requestCode Application specific request code to match with a result
@@ -1275,20 +1256,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
}
/**
- * Gets whether you should show UI with rationale for requesting a permission.
- * You should do this only if you do not have the permission and the context in
- * which the permission is requested does not clearly communicate to the user
- * what would be the benefit from granting this permission.
- *
- * For example, if you write a camera app, requesting the camera permission
- * would be expected by the user and no rationale for why it is requested is
- * needed. If however, the app needs location for tagging photos then a non-tech
- * savvy user may wonder how location is related to taking photos. In this case
- * you may choose to show UI with rationale of requesting this permission.
- *
+ * Gets whether you should show UI with rationale before requesting a permission.
*
* @param permission A permission your app wants to request.
- * @return Whether you can show permission rationale UI.
+ * @return Whether you should show permission rationale UI.
*
* @see Context#checkSelfPermission(String)
* @see #requestPermissions(String[], int)
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e84c5e574713efe2ccce187b8bf055a720b5d2bc..945957738f8eb30ba9a172caa4a3fa89b226527f 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -677,4 +677,10 @@ interface IActivityManager {
* Return whether the app freezer is supported (true) or not (false) by this system.
*/
boolean isAppFreezerSupported();
+
+
+ /**
+ * Kills uid with the reason of permission change.
+ */
+ void killUidForPermissionChange(int appId, int userId, String reason);
}
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 2d06ee8d06bcc6ad16ab6db602c5028f99766602..aec9f3e98960bcb3a13c18301b4a50950b8b3cc0 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -216,4 +216,16 @@ oneway interface ITaskStackListener {
* in {@link android.content.pm.ActivityInfo}.
*/
void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation);
+
+ /**
+ * Called when a rotation is about to start on the foreground activity.
+ * This applies for:
+ * * free sensor rotation
+ * * forced rotation
+ * * rotation settings set through adb command line
+ * * rotation that occurs when rotation tile is toggled in quick settings
+ *
+ * @param displayId id of the display where activity will rotate
+ */
+ void onActivityRotation(int displayId);
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e233adeb3f65a69bdadfb5aaa13bbe2097f5eda8..721525d9af9d7b7466e0111a910d1feb956f0ed7 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1718,7 +1718,7 @@ public class Instrumentation {
}
}
try {
- intent.migrateExtraStreamToClipData();
+ intent.migrateExtraStreamToClipData(who);
intent.prepareToLeaveProcess(who);
int result = ActivityTaskManager.getService().startActivity(whoThread,
who.getBasePackageName(), who.getAttributionTag(), intent,
@@ -1788,7 +1788,7 @@ public class Instrumentation {
try {
String[] resolvedTypes = new String[intents.length];
for (int i=0; i dataOnlyInputs = new ArrayList<>();
- RemoteInput[] previousDataInputs =
- (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
+ RemoteInput[] previousDataInputs = getParcelableArrayFromBundle(
+ mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class);
if (previousDataInputs != null) {
for (RemoteInput input : previousDataInputs) {
dataOnlyInputs.add(input);
@@ -5368,8 +5368,8 @@ public class Notification implements Parcelable
big.setViewVisibility(R.id.actions_container, View.GONE);
}
- RemoteInputHistoryItem[] replyText = (RemoteInputHistoryItem[])
- mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ RemoteInputHistoryItem[] replyText = getParcelableArrayFromBundle(
+ mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class);
if (validRemoteInput && replyText != null && replyText.length > 0
&& !TextUtils.isEmpty(replyText[0].getText())
&& p.maxRemoteInputHistory > 0) {
@@ -7495,6 +7495,7 @@ public class Notification implements Parcelable
mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION);
mUnreadMessageCount = extras.getInt(EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT);
+ mShortcutIcon = extras.getParcelable(EXTRA_CONVERSATION_ICON);
}
/**
@@ -7625,9 +7626,7 @@ public class Notification implements Parcelable
}
boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY;
boolean isImportantConversation = mConversationType == CONVERSATION_TYPE_IMPORTANT;
- Icon largeIcon = isConversationLayout && mShortcutIcon != null
- ? mShortcutIcon
- : mBuilder.mN.mLargeIcon;
+ Icon largeIcon = mBuilder.mN.mLargeIcon;
TemplateBindResult bindResult = new TemplateBindResult();
StandardTemplateParams p = mBuilder.mParams.reset()
.hasProgress(false)
@@ -7671,6 +7670,8 @@ public class Notification implements Parcelable
contentView.setCharSequence(R.id.status_bar_latest_event_content,
"setConversationTitle", conversationTitle);
if (isConversationLayout) {
+ contentView.setIcon(R.id.status_bar_latest_event_content,
+ "setShortcutIcon", mShortcutIcon);
contentView.setBoolean(R.id.status_bar_latest_event_content,
"setIsImportantConversation", isImportantConversation);
}
@@ -8154,8 +8155,9 @@ public class Notification implements Parcelable
if (mBuilder.mActions.size() > 0) {
maxRows--;
}
- RemoteInputHistoryItem[] remoteInputHistory = (RemoteInputHistoryItem[])
- mBuilder.mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ RemoteInputHistoryItem[] remoteInputHistory = getParcelableArrayFromBundle(
+ mBuilder.mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS,
+ RemoteInputHistoryItem.class);
if (remoteInputHistory != null
&& remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) {
// Let's remove some messages to make room for the remote input history.
@@ -9578,8 +9580,8 @@ public class Notification implements Parcelable
mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
- Notification[] pages = getNotificationArrayFromBundle(
- wearableBundle, KEY_PAGES);
+ Notification[] pages = getParcelableArrayFromBundle(
+ wearableBundle, KEY_PAGES, Notification.class);
if (pages != null) {
Collections.addAll(mPages, pages);
}
@@ -10837,17 +10839,22 @@ public class Notification implements Parcelable
}
/**
- * Get an array of Notification objects from a parcelable array bundle field.
+ * Get an array of Parcelable objects from a parcelable array bundle field.
* Update the bundle to have a typed array so fetches in the future don't need
* to do an array copy.
*/
- private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
- Parcelable[] array = bundle.getParcelableArray(key);
- if (array instanceof Notification[] || array == null) {
- return (Notification[]) array;
+ @Nullable
+ private static T[] getParcelableArrayFromBundle(
+ Bundle bundle, String key, Class itemClass) {
+ final Parcelable[] array = bundle.getParcelableArray(key);
+ final Class> arrayClass = Array.newInstance(itemClass, 0).getClass();
+ if (arrayClass.isInstance(array) || array == null) {
+ return (T[]) array;
+ }
+ final T[] typedArray = (T[]) Array.newInstance(itemClass, array.length);
+ for (int i = 0; i < array.length; i++) {
+ typedArray[i] = (T) array[i];
}
- Notification[] typedArray = Arrays.copyOf(array, array.length,
- Notification[].class);
bundle.putParcelableArray(key, typedArray);
return typedArray;
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 792f840638a9db4d761078e9ad745e54ff41044b..cd352e141994388e16dbb69d6ac3487a16e3241a 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -351,7 +351,7 @@ public final class PendingIntent implements Parcelable {
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
try {
- intent.migrateExtraStreamToClipData();
+ intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
@@ -377,7 +377,7 @@ public final class PendingIntent implements Parcelable {
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
try {
- intent.migrateExtraStreamToClipData();
+ intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
@@ -491,7 +491,7 @@ public final class PendingIntent implements Parcelable {
String packageName = context.getPackageName();
String[] resolvedTypes = new String[intents.length];
for (int i=0; i> sEmptyReferencePredicate =
- weakRef -> weakRef == null || weakRef.get() == null;
-
/**
* The global compatibility settings.
*/
@@ -100,6 +97,7 @@ public class ResourcesManager {
*/
@UnsupportedAppUsage
private final ArrayList> mResourceReferences = new ArrayList<>();
+ private final ReferenceQueue mResourcesReferencesQueue = new ReferenceQueue<>();
private static class ApkKey {
public final String path;
@@ -155,6 +153,7 @@ public class ResourcesManager {
}
public final Configuration overrideConfig = new Configuration();
public final ArrayList> activityResources = new ArrayList<>();
+ final ReferenceQueue activityResourcesQueue = new ReferenceQueue<>();
}
/**
@@ -667,12 +666,15 @@ public class ResourcesManager {
@NonNull CompatibilityInfo compatInfo) {
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
activityToken);
+ cleanupReferences(activityResources.activityResources,
+ activityResources.activityResourcesQueue);
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
resources.setCallbacks(mUpdateCallbacks);
- activityResources.activityResources.add(new WeakReference<>(resources));
+ activityResources.activityResources.add(
+ new WeakReference<>(resources, activityResources.activityResourcesQueue));
if (DEBUG) {
Slog.d(TAG, "- creating new ref=" + resources);
Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
@@ -682,11 +684,13 @@ public class ResourcesManager {
private @NonNull Resources createResourcesLocked(@NonNull ClassLoader classLoader,
@NonNull ResourcesImpl impl, @NonNull CompatibilityInfo compatInfo) {
+ cleanupReferences(mResourceReferences, mResourcesReferencesQueue);
+
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
resources.setCallbacks(mUpdateCallbacks);
- mResourceReferences.add(new WeakReference<>(resources));
+ mResourceReferences.add(new WeakReference<>(resources, mResourcesReferencesQueue));
if (DEBUG) {
Slog.d(TAG, "- creating new ref=" + resources);
Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
@@ -752,7 +756,6 @@ public class ResourcesManager {
updateResourcesForActivity(token, overrideConfig, displayId,
false /* movedToDifferentDisplay */);
- cleanupReferences(token);
rebaseKeyForActivity(token, key);
synchronized (this) {
@@ -778,10 +781,6 @@ public class ResourcesManager {
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
- // Clean up any dead references so they don't pile up.
- ArrayUtils.unstableRemoveIf(activityResources.activityResources,
- sEmptyReferencePredicate);
-
// Rebase the key's override config on top of the Activity's base override.
if (key.hasOverrideConfiguration()
&& !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
@@ -794,21 +793,21 @@ public class ResourcesManager {
/**
* Check WeakReferences and remove any dead references so they don't pile up.
- * @param activityToken optional token to clean up Activity resources
*/
- private void cleanupReferences(IBinder activityToken) {
- synchronized (this) {
- if (activityToken != null) {
- ActivityResources activityResources = mActivityResourceReferences.get(
- activityToken);
- if (activityResources != null) {
- ArrayUtils.unstableRemoveIf(activityResources.activityResources,
- sEmptyReferencePredicate);
- }
- } else {
- ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
- }
+ private static void cleanupReferences(ArrayList> references,
+ ReferenceQueue referenceQueue) {
+ Reference extends T> enduedRef = referenceQueue.poll();
+ if (enduedRef == null) {
+ return;
}
+
+ final HashSet> deadReferences = new HashSet<>();
+ for (; enduedRef != null; enduedRef = referenceQueue.poll()) {
+ deadReferences.add(enduedRef);
+ }
+
+ ArrayUtils.unstableRemoveIf(references,
+ (ref) -> ref == null || deadReferences.contains(ref));
}
/**
@@ -896,8 +895,6 @@ public class ResourcesManager {
loaders == null ? null : loaders.toArray(new ResourcesLoader[0]));
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
- cleanupReferences(activityToken);
-
if (activityToken != null) {
rebaseKeyForActivity(activityToken, key);
}
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 8ad33dbf9f4a9c520018f2a9efaf88aa3c5aecc7..b65ae7a0a7b94f18ffeb8393966d096d63477f9f 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -27,6 +27,15 @@
}
]
},
+ {
+ "file_patterns": ["(/|^)AppOpsManager.java"],
+ "name": "CtsStatsdHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.cts.statsd.atom.UidAtomTests#testAppOps"
+ }
+ ]
+ },
{
"file_patterns": ["(/|^)AppOpsManager.java"],
"name": "CtsPermission2TestCases",
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index c7a2a1e11c9eb3c4dcaad7f0a4378e426435c844..f3f00e50715b31b4014776df8958dd4c43ace293 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -16,8 +16,6 @@
package android.app;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -170,6 +168,14 @@ public class TaskInfo {
@Nullable
public ActivityInfo topActivityInfo;
+ /**
+ * Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity
+ * supports), this is what the system actually uses for resizability based on other policy and
+ * developer options.
+ * @hide
+ */
+ public boolean isResizeable;
+
TaskInfo() {
// Do nothing
}
@@ -192,11 +198,6 @@ public class TaskInfo {
}
}
- /** @hide */
- public boolean isResizable() {
- return resizeMode != RESIZE_MODE_UNRESIZEABLE;
- }
-
/** @hide */
@NonNull
@TestApi
@@ -245,6 +246,7 @@ public class TaskInfo {
topActivityInfo = source.readInt() != 0
? ActivityInfo.CREATOR.createFromParcel(source)
: null;
+ isResizeable = source.readBoolean();
}
/**
@@ -294,6 +296,7 @@ public class TaskInfo {
dest.writeInt(1);
topActivityInfo.writeToParcel(dest, flags);
}
+ dest.writeBoolean(isResizeable);
}
@Override
@@ -308,6 +311,7 @@ public class TaskInfo {
+ " lastActiveTime=" + lastActiveTime
+ " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
+ " resizeMode=" + resizeMode
+ + " isResizeable=" + isResizeable
+ " token=" + token
+ " topActivityType=" + topActivityType
+ " pictureInPictureParams=" + pictureInPictureParams
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 5d8daf88a8de6c6e134d1655511bd4e1386d2402..f137d6858a689bec88d7b7c8723780061ac71095 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -199,4 +199,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
@Override
public void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation) {
}
+
+ @Override
+ public void onActivityRotation(int displayId) {
+ }
}
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 82e988109db8e4db4eb55f42d927959ce7a5882c..ce51dba76780ce08f3f3d7c6b4b0f765f9197ed6 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -294,7 +294,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
}
final long identity = Binder.clearCallingIdentity();
try {
- mPermissionManager.revokeRuntimePermission(packageName, permission, userId);
+ mPermissionManager.revokeRuntimePermission(packageName, permission, userId, null);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java
index 4d9970c2c144f17a44ee26bf95974b4f365c38d8..15ff531b445d985573c1e4371845f33e17d6f272 100644
--- a/core/java/android/app/admin/DevicePolicyCache.java
+++ b/core/java/android/app/admin/DevicePolicyCache.java
@@ -41,7 +41,8 @@ public abstract class DevicePolicyCache {
/**
* See {@link DevicePolicyManager#getScreenCaptureDisabled}
*/
- public abstract boolean getScreenCaptureDisabled(@UserIdInt int userHandle);
+ public abstract boolean isScreenCaptureAllowed(@UserIdInt int userHandle,
+ boolean ownerCanAddInternalSystemWindow);
/**
* Caches {@link DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} of the
@@ -56,8 +57,9 @@ public abstract class DevicePolicyCache {
private static final EmptyDevicePolicyCache INSTANCE = new EmptyDevicePolicyCache();
@Override
- public boolean getScreenCaptureDisabled(int userHandle) {
- return false;
+ public boolean isScreenCaptureAllowed(int userHandle,
+ boolean ownerCanAddInternalSystemWindow) {
+ return true;
}
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c65064324c8cdd03890dc875b98550c08d27b03f..322cac81d58b3b5739b5ac616ef05983b082735a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4247,6 +4247,12 @@ public class DevicePolicyManager {
* device. After this method is called, the device must be unlocked using strong authentication
* (PIN, pattern, or password). This API is intended for use only by device admins.
*
+ * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have
+ * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is
+ * true, then the method will return without completing any action. Before version
+ * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature,
+ * regardless of the caller's permissions.
+ *
* The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
* to be able to call this method; if it has not, a security exception will be thrown.
*
@@ -4274,6 +4280,12 @@ public class DevicePolicyManager {
* device. After this method is called, the device must be unlocked using strong authentication
* (PIN, pattern, or password). This API is intended for use only by device admins.
*
+ * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have
+ * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is
+ * true, then the method will return without completing any action. Before version
+ * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature,
+ * regardless of the caller's permissions.
+ *
* The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
* to be able to call this method; if it has not, a security exception will be thrown.
*
@@ -5868,12 +5880,22 @@ public class DevicePolicyManager {
* returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be
* the profile owner of an organization-owned managed profile.
*
- * If the caller is device owner or called on the parent instance, then the
- * restriction will be applied to all users.
+ * If the caller is device owner, then the restriction will be applied to all users. If
+ * called on the parent instance, then the restriction will be applied on the personal profile.
*
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} to be able to call this method; if it has
* not, a security exception will be thrown.
+ *
+ * Note, this policy type is deprecated for legacy device admins since
+ * {@link android.os.Build.VERSION_CODES#Q}. On Android
+ * {@link android.os.Build.VERSION_CODES#Q} devices, legacy device admins targeting SDK
+ * version {@link android.os.Build.VERSION_CODES#P} or below can still call this API to
+ * disable camera, while legacy device admins targeting SDK version
+ * {@link android.os.Build.VERSION_CODES#Q} will receive a SecurityException. Starting
+ * from Android {@link android.os.Build.VERSION_CODES#R}, requests to disable camera from
+ * legacy device admins targeting SDK version {@link android.os.Build.VERSION_CODES#P} or
+ * below will be silently ignored.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param disabled Whether or not the camera should be disabled.
diff --git a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
index bea1bd6e70d6c76e16084431d04a86729d83f2d7..b3f9e31abaa444db1d990d805e8199487b46e780 100644
--- a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
+++ b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
@@ -95,7 +95,7 @@ public final class ContentSuggestionsManager {
try {
mService.provideContextBitmap(mUser, bitmap, imageContextRequestExtras);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -117,7 +117,7 @@ public final class ContentSuggestionsManager {
try {
mService.provideContextImage(mUser, taskId, imageContextRequestExtras);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -146,7 +146,7 @@ public final class ContentSuggestionsManager {
mService.suggestContentSelections(
mUser, request, new SelectionsCallbackWrapper(callback, callbackExecutor));
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -173,7 +173,7 @@ public final class ContentSuggestionsManager {
mService.classifyContentSelections(
mUser, request, new ClassificationsCallbackWrapper(callback, callbackExecutor));
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -193,7 +193,7 @@ public final class ContentSuggestionsManager {
try {
mService.notifyInteraction(mUser, requestId, interaction);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -213,9 +213,10 @@ public final class ContentSuggestionsManager {
mService.isEnabled(mUser, receiver);
return receiver.getIntResult() != 0;
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get the enable status.");
}
- return false;
}
/**
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index d6e77624a967e8cdf8f13fb7d4eaf6a6cafe240d..fc8248e1012af07b12e9f2e893fb93cc3cb9292a 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -41,6 +41,7 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.telephony.TelephonyManager;
import android.util.DataUnit;
import android.util.Log;
@@ -198,6 +199,12 @@ public class NetworkStatsManager {
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ *
Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
@@ -231,6 +238,12 @@ public class NetworkStatsManager {
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ *
Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
@@ -268,6 +281,12 @@ public class NetworkStatsManager {
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ *
Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
@@ -301,7 +320,7 @@ public class NetworkStatsManager {
/**
* Query network usage statistics details for a given uid.
*
- * #see queryDetailsForUidTagState(int, String, long, long, int, int, int)
+ * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
long startTime, long endTime, int uid) throws SecurityException {
@@ -319,7 +338,7 @@ public class NetworkStatsManager {
/**
* Query network usage statistics details for a given uid and tag.
*
- * #see queryDetailsForUidTagState(int, String, long, long, int, int, int)
+ * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
long startTime, long endTime, int uid, int tag) throws SecurityException {
@@ -344,6 +363,12 @@ public class NetworkStatsManager {
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ *
Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
@@ -398,6 +423,12 @@ public class NetworkStatsManager {
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ *
Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
@@ -455,7 +486,7 @@ public class NetworkStatsManager {
/**
* Registers to receive notifications about data usage on specified networks.
*
- * #see registerUsageCallback(int, String[], long, UsageCallback, Handler)
+ * @see #registerUsageCallback(int, String, long, UsageCallback, Handler)
*/
public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
UsageCallback callback) {
@@ -472,6 +503,12 @@ public class NetworkStatsManager {
* @param networkType Type of network to monitor. Either
{@link ConnectivityManager#TYPE_MOBILE} or {@link ConnectivityManager#TYPE_WIFI}.
* @param subscriberId If applicable, the subscriber id of the network interface.
+ *
Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when registering for the mobile network type to receive
+ * notifications for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
* @param thresholdBytes Threshold in bytes to be notified on.
* @param callback The {@link UsageCallback} that the system will call when data usage
* has exceeded the specified threshold.
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0f999ad68a626dd2611e4269699c35e858b13acf..3522b1b8aff5756cadb165f3bdfa43d169aafa05 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -133,6 +133,7 @@ public final class UsageEvents implements Parcelable {
/**
* An event type denoting that a component was in the foreground when the stats
* rolled-over. This is effectively treated as a {@link #ACTIVITY_PAUSED}.
+ * This event has a non-null packageName, and a null className.
* {@hide}
*/
public static final int END_OF_DAY = 3;
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 2ce6a86753a6e449c54e877d7aa3ac94eb7059c2..8a6cc95319fbbca3352ad601679df91db4e43277 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -154,6 +154,7 @@ public final class UsageStatsManager {
* been misbehaving in some manner.
* Apps in this bucket will have the most restrictions, including network restrictions and
* additional restrictions on jobs.
+ *
Note: this bucket is not enabled in {@link Build.VERSION_CODES#R}.
* @see #getAppStandbyBucket()
*/
public static final int STANDBY_BUCKET_RESTRICTED = 45;
diff --git a/core/java/android/companion/BluetoothDeviceFilterUtils.java b/core/java/android/companion/BluetoothDeviceFilterUtils.java
index 24be45cb20fe9397b48ce57a876fd098d30bc3c3..8e687413b7e1bb88d3f6403142b3ac5fff8ea135 100644
--- a/core/java/android/companion/BluetoothDeviceFilterUtils.java
+++ b/core/java/android/companion/BluetoothDeviceFilterUtils.java
@@ -51,13 +51,6 @@ public class BluetoothDeviceFilterUtils {
return s == null ? null : Pattern.compile(s);
}
- static boolean matches(ScanFilter filter, BluetoothDevice device) {
- boolean result = matchesAddress(filter.getDeviceAddress(), device)
- && matchesServiceUuid(filter.getServiceUuid(), filter.getServiceUuidMask(), device);
- if (DEBUG) debugLogMatchResult(result, device, filter);
- return result;
- }
-
static boolean matchesAddress(String deviceAddress, BluetoothDevice device) {
final boolean result = deviceAddress == null
|| (device != null && deviceAddress.equals(device.getAddress()));
diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java
index dccfb0346c9c2576dc06e6c0e85f54759027cc2b..8c071fe9910461a52a424883079204f3ea05db80 100644
--- a/core/java/android/companion/BluetoothLeDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLeDeviceFilter.java
@@ -37,7 +37,6 @@ import android.util.Log;
import com.android.internal.util.BitUtils;
import com.android.internal.util.ObjectUtils;
-import com.android.internal.util.Preconditions;
import libcore.util.HexEncoding;
@@ -166,21 +165,18 @@ public final class BluetoothLeDeviceFilter implements DeviceFilter {
/** @hide */
@Override
- public boolean matches(ScanResult device) {
- boolean result = matches(device.getDevice())
+ public boolean matches(ScanResult scanResult) {
+ BluetoothDevice device = scanResult.getDevice();
+ boolean result = getScanFilter().matches(scanResult)
+ && BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device)
&& (mRawDataFilter == null
- || BitUtils.maskedEquals(device.getScanRecord().getBytes(),
+ || BitUtils.maskedEquals(scanResult.getScanRecord().getBytes(),
mRawDataFilter, mRawDataFilterMask));
if (DEBUG) Log.i(LOG_TAG, "matches(this = " + this + ", device = " + device +
") -> " + result);
return result;
}
- private boolean matches(BluetoothDevice device) {
- return BluetoothDeviceFilterUtils.matches(getScanFilter(), device)
- && BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device);
- }
-
/** @hide */
@Override
public int getMediumType() {
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 4bd7b059dfaac42b1552a9a015c8e35ab70a50b4..591a714bfb930ddbdab1b26a07a70ef850101683 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -65,6 +65,13 @@ public final class CompanionDeviceManager {
/**
* A device, returned in the activity result of the {@link IntentSender} received in
* {@link Callback#onDeviceFound}
+ *
+ * Type is:
+ *
+ *
for classic Bluetooth - {@link android.bluetooth.BluetoothDevice}
+ *
for Bluetooth LE - {@link android.bluetooth.le.ScanResult}
+ *
for WiFi - {@link android.net.wifi.ScanResult}
+ *
*/
public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 5dc41e483640a81f9cb7c7d3303c76ef4e30c7fb..89abfc95d634c7ea18067ef0f4ba30683635329a 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1151,6 +1151,9 @@ public class ContextWrapper extends Context {
*/
@Override
public boolean isUiContext() {
+ if (mBase == null) {
+ return false;
+ }
return mBase.isUiContext();
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index baaf8f76797a66dbf42e7c9f9285df7070803603..ededd0d2ea30e553fa3ffa0e76139025d794f000 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1884,6 +1884,9 @@ public class Intent implements Parcelable, Cloneable {
/**
* Activity action: Launch UI to manage auto-revoke state.
+ *
+ * This is equivalent to Intent#ACTION_APPLICATION_DETAILS_SETTINGS
+ *
*
* Input: {@link Intent#setData data} should be a {@code package}-scheme {@link Uri} with
* a package name, whose auto-revoke state will be reviewed (mandatory).
@@ -6442,7 +6445,7 @@ public class Intent implements Parcelable, Cloneable {
public static final int FLAG_ACTIVITY_RETAIN_IN_RECENTS = 0x00002000;
/**
- * This flag is only used in split-screen multi-window mode. The new activity will be displayed
+ * This flag is only used for split-screen multi-window mode. The new activity will be displayed
* adjacent to the one launching it. This can only be used in conjunction with
* {@link #FLAG_ACTIVITY_NEW_TASK}. Also, setting {@link #FLAG_ACTIVITY_MULTIPLE_TASK} is
* required if you want a new instance of an existing activity to be created.
@@ -11272,6 +11275,19 @@ public class Intent implements Parcelable, Cloneable {
* @hide
*/
public boolean migrateExtraStreamToClipData() {
+ return migrateExtraStreamToClipData(AppGlobals.getInitialApplication());
+ }
+
+ /**
+ * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and
+ * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested
+ * intents in {@link #ACTION_CHOOSER}.
+ *
+ * @param context app context
+ * @return Whether any contents were migrated.
+ * @hide
+ */
+ public boolean migrateExtraStreamToClipData(Context context) {
// Refuse to touch if extras already parcelled
if (mExtras != null && mExtras.isParcelled()) return false;
@@ -11289,7 +11305,7 @@ public class Intent implements Parcelable, Cloneable {
try {
final Intent intent = getParcelableExtra(EXTRA_INTENT);
if (intent != null) {
- migrated |= intent.migrateExtraStreamToClipData();
+ migrated |= intent.migrateExtraStreamToClipData(context);
}
} catch (ClassCastException e) {
}
@@ -11299,7 +11315,7 @@ public class Intent implements Parcelable, Cloneable {
for (int i = 0; i < intents.length; i++) {
final Intent intent = (Intent) intents[i];
if (intent != null) {
- migrated |= intent.migrateExtraStreamToClipData();
+ migrated |= intent.migrateExtraStreamToClipData(context);
}
}
}
@@ -11362,13 +11378,17 @@ public class Intent implements Parcelable, Cloneable {
} catch (ClassCastException e) {
}
} else if (isImageCaptureIntent()) {
- final Uri output;
+ Uri output;
try {
output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
} catch (ClassCastException e) {
return false;
}
+
if (output != null) {
+ output = maybeConvertFileToContentUri(context, output);
+ putExtra(MediaStore.EXTRA_OUTPUT, output);
+
setClipData(ClipData.newRawUri("", output));
addFlags(FLAG_GRANT_WRITE_URI_PERMISSION|FLAG_GRANT_READ_URI_PERMISSION);
return true;
@@ -11378,6 +11398,23 @@ public class Intent implements Parcelable, Cloneable {
return false;
}
+ private Uri maybeConvertFileToContentUri(Context context, Uri uri) {
+ if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())
+ && context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.R) {
+ File file = new File(uri.getPath());
+ try {
+ if (!file.exists()) file.createNewFile();
+ uri = MediaStore.scanFile(context.getContentResolver(), new File(uri.getPath()));
+ if (uri != null) {
+ return uri;
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Ignoring failure to create file " + file, e);
+ }
+ }
+ return uri;
+ }
+
/**
* Convert the dock state to a human readable format.
* @hide
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 79da1f6ab2826602aad5d7bb38a1c72daf408904..ee9bd3d259fb803978b58a566bce011ebaabee92 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1168,7 +1168,12 @@ public class IntentFilter implements Parcelable {
public int match(Uri data, boolean wildcardSupported) {
String host = data.getHost();
if (host == null) {
- return NO_MATCH_DATA;
+ if (wildcardSupported && mWild) {
+ // special case, if no host is provided, but the Authority is wildcard, match
+ return MATCH_CATEGORY_HOST;
+ } else {
+ return NO_MATCH_DATA;
+ }
}
if (false) Log.v("IntentFilter",
"Match host " + host + ": " + mHost);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index bd1ee27ece9e8f0309216cacf672986742cb68bb..1a694b34474a6f918ee694db9832b5c9be105aa9 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1243,14 +1243,7 @@ public class LauncherApps {
private ParcelFileDescriptor getUriShortcutIconFd(@NonNull String packageName,
@NonNull String shortcutId, int userId) {
- String uri = null;
- try {
- uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId,
- userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
+ String uri = getShortcutIconUri(packageName, shortcutId, userId);
if (uri == null) {
return null;
}
@@ -1262,6 +1255,18 @@ public class LauncherApps {
}
}
+ private String getShortcutIconUri(@NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ String uri = null;
+ try {
+ uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId,
+ userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return uri;
+ }
+
/**
* Returns the icon for this shortcut, without any badging for the profile.
*
@@ -1357,6 +1362,17 @@ public class LauncherApps {
} catch (IOException ignore) {
}
}
+ } else if (shortcut.hasIconUri()) {
+ String uri = getShortcutIconUri(shortcut.getPackage(), shortcut.getId(),
+ shortcut.getUserId());
+ if (uri == null) {
+ return null;
+ }
+ if (shortcut.hasAdaptiveBitmap()) {
+ return Icon.createWithAdaptiveBitmapContentUri(uri);
+ } else {
+ return Icon.createWithContentUri(uri);
+ }
} else if (shortcut.hasIconResource()) {
return Icon.createWithResource(shortcut.getPackage(), shortcut.getIconResourceId());
} else {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index ed75504529b922587c1c0cb16cba182f9f96c351..fc4ccd072e758af9c13a3cc574452e1bd467b9bf 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1449,6 +1449,13 @@ public class PackageInstaller {
/** {@hide} */
public static final int UID_UNKNOWN = -1;
+ /**
+ * This value is derived from the maximum file name length. No package above this limit
+ * can ever be successfully installed on the device.
+ * @hide
+ */
+ public static final int MAX_PACKAGE_NAME_LENGTH = 255;
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int mode = MODE_INVALID;
@@ -1642,6 +1649,8 @@ public class PackageInstaller {
/**
* Optionally set a label representing the app being installed.
+ *
+ * This value will be trimmed to the first 1000 characters.
*/
public void setAppLabel(@Nullable CharSequence appLabel) {
this.appLabel = (appLabel != null) ? appLabel.toString() : null;
@@ -1711,7 +1720,8 @@ public class PackageInstaller {
*
*
Initially, all restricted permissions are whitelisted but you can change
* which ones are whitelisted by calling this method or the corresponding ones
- * on the {@link PackageManager}.
+ * on the {@link PackageManager}. Only soft or hard restricted permissions on the current
+ * Android version are supported and any invalid entries will be removed.
*
* @see PackageManager#addWhitelistedRestrictedPermission(String, String, int)
* @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int)
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index f354bdb5a08b4433b2f9283fbab37fb5a4d80cfd..65ce1e7ef07962ef8208bcb0d740425d073e8d64 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -49,8 +49,16 @@ import java.util.Objects;
* in the implementation of Parcelable in subclasses.
*/
public class PackageItemInfo {
- /** The maximum length of a safe label, in characters */
- private static final int MAX_SAFE_LABEL_LENGTH = 50000;
+
+ /**
+ * The maximum length of a safe label, in characters
+ *
+ * TODO(b/157997155): It may make sense to expose this publicly so that apps can check for the
+ * value and truncate the strings/use a different label, without having to hardcode and make
+ * assumptions about the value.
+ * @hide
+ */
+ public static final int MAX_SAFE_LABEL_LENGTH = 1000;
/** @hide */
public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c577d0e896b01fc2d9fdd02efd7e8c82a2432838..ea4a2a0b8c35e3209b66e1b0706a7c66b820fc2b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4666,8 +4666,7 @@ public abstract class PackageManager {
* Marks an application exempt from having its permissions be automatically revoked when
* the app is unused for an extended period of time.
*
- * Only the installer on record that installed the given package, or a holder of
- * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+ * Only the installer on record that installed the given package is allowed to call this.
*
* Packages start in whitelisted state, and it is the installer's responsibility to
* un-whitelist the packages it installs, unless auto-revoking permissions from that package
@@ -8172,7 +8171,7 @@ public abstract class PackageManager {
private static final PropertyInvalidatedCache
sPackageInfoCache =
new PropertyInvalidatedCache(
- 16, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
+ 32, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
@Override
protected PackageInfo recompute(PackageInfoQuery query) {
return getPackageInfoAsUserUncached(
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c8dd4d9d9d51d0a8e1b5ddc31b5455b24f055e31..70e4e6cbf622eb6bb145c2183ed5ced8b10da55d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -205,6 +205,7 @@ public class PackageParser {
public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
public static final String TAG_USES_SDK = "uses-sdk";
public static final String TAG_USES_SPLIT = "uses-split";
+ public static final String TAG_PROFILEABLE = "profileable";
public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
@@ -459,6 +460,9 @@ public class PackageParser {
public final SigningDetails signingDetails;
public final boolean coreApp;
public final boolean debuggable;
+ // This does not represent the actual manifest structure since the 'profilable' tag
+ // could be used with attributes other than 'shell'. Extend if necessary.
+ public final boolean profilableByShell;
public final boolean multiArch;
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
@@ -470,15 +474,13 @@ public class PackageParser {
public final int overlayPriority;
public ApkLite(String codePath, String packageName, String splitName,
- boolean isFeatureSplit,
- String configForSplit, String usesSplitName, boolean isSplitRequired,
- int versionCode, int versionCodeMajor,
- int revisionCode, int installLocation, List verifiers,
- SigningDetails signingDetails, boolean coreApp,
- boolean debuggable, boolean multiArch, boolean use32bitAbi,
- boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
- String targetPackageName, boolean overlayIsStatic, int overlayPriority,
- int minSdkVersion, int targetSdkVersion) {
+ boolean isFeatureSplit, String configForSplit, String usesSplitName,
+ boolean isSplitRequired, int versionCode, int versionCodeMajor, int revisionCode,
+ int installLocation, List verifiers, SigningDetails signingDetails,
+ boolean coreApp, boolean debuggable, boolean profilableByShell, boolean multiArch,
+ boolean use32bitAbi, boolean useEmbeddedDex, boolean extractNativeLibs,
+ boolean isolatedSplits, String targetPackageName, boolean overlayIsStatic,
+ int overlayPriority, int minSdkVersion, int targetSdkVersion) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -493,6 +495,7 @@ public class PackageParser {
this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
this.coreApp = coreApp;
this.debuggable = debuggable;
+ this.profilableByShell = profilableByShell;
this.multiArch = multiArch;
this.use32bitAbi = use32bitAbi;
this.useEmbeddedDex = useEmbeddedDex;
@@ -1374,9 +1377,11 @@ public class PackageParser {
}
SigningDetails verified;
if (skipVerify) {
- // systemDir APKs are already trusted, save time by not verifying
+ // systemDir APKs are already trusted, save time by not verifying; since the signature
+ // is not verified and some system apps can have their V2+ signatures stripped allow
+ // pulling the certs from the jar signature.
verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
- apkPath, minSignatureScheme);
+ apkPath, SigningDetails.SignatureSchemeVersion.JAR);
} else {
verified = ApkSignatureVerifier.verify(apkPath, minSignatureScheme);
}
@@ -1573,6 +1578,7 @@ public class PackageParser {
int revisionCode = 0;
boolean coreApp = false;
boolean debuggable = false;
+ boolean profilableByShell = false;
boolean multiArch = false;
boolean use32bitAbi = false;
boolean extractNativeLibs = true;
@@ -1638,6 +1644,10 @@ public class PackageParser {
final String attr = attrs.getAttributeName(i);
if ("debuggable".equals(attr)) {
debuggable = attrs.getAttributeBooleanValue(i, false);
+ if (debuggable) {
+ // Debuggable implies profileable
+ profilableByShell = true;
+ }
}
if ("multiArch".equals(attr)) {
multiArch = attrs.getAttributeBooleanValue(i, false);
@@ -1690,6 +1700,13 @@ public class PackageParser {
minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
}
}
+ } else if (TAG_PROFILEABLE.equals(parser.getName())) {
+ for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+ final String attr = attrs.getAttributeName(i);
+ if ("shell".equals(attr)) {
+ profilableByShell = attrs.getAttributeBooleanValue(i, profilableByShell);
+ }
+ }
}
}
@@ -1707,8 +1724,9 @@ public class PackageParser {
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
- multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits,
- targetPackage, overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion);
+ profilableByShell, multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs,
+ isolatedSplits, targetPackage, overlayIsStatic, overlayPriority, minSdkVersion,
+ targetSdkVersion);
}
/**
diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java
index 653b9ec9e8f23c48e6ac0e5975d634c1a8e45330..98a20f73a1200a2345abcccdf22472bb95830937 100644
--- a/core/java/android/content/pm/PackagePartitions.java
+++ b/core/java/android/content/pm/PackagePartitions.java
@@ -183,17 +183,20 @@ public class PackagePartitions {
/** Returns whether the partition contains the specified file in its priv-app folder. */
public boolean containsPrivApp(@NonNull File scanFile) {
- return FileUtils.contains(mPrivAppFolder.getFile(), canonicalize(scanFile));
+ return mPrivAppFolder != null
+ && FileUtils.contains(mPrivAppFolder.getFile(), canonicalize(scanFile));
}
/** Returns whether the partition contains the specified file in its app folder. */
public boolean containsApp(@NonNull File scanFile) {
- return FileUtils.contains(mAppFolder.getFile(), canonicalize(scanFile));
+ return mAppFolder != null
+ && FileUtils.contains(mAppFolder.getFile(), canonicalize(scanFile));
}
/** Returns whether the partition contains the specified file in its overlay folder. */
public boolean containsOverlay(@NonNull File scanFile) {
- return FileUtils.contains(mOverlayFolder.getFile(), canonicalize(scanFile));
+ return mOverlayFolder != null
+ && FileUtils.contains(mOverlayFolder.getFile(), canonicalize(scanFile));
}
}
diff --git a/core/java/android/content/pm/dex/ArtManagerInternal.java b/core/java/android/content/pm/dex/ArtManagerInternal.java
index 62ab9e02f85804f8fbcba447a245cd875559cd4c..23fef29803e756e8453ec867b8ed01bebea0c409 100644
--- a/core/java/android/content/pm/dex/ArtManagerInternal.java
+++ b/core/java/android/content/pm/dex/ArtManagerInternal.java
@@ -30,5 +30,5 @@ public abstract class ArtManagerInternal {
* in executes using the specified {@code abi}.
*/
public abstract PackageOptimizationInfo getPackageOptimizationInfo(
- ApplicationInfo info, String abi);
+ ApplicationInfo info, String abi, String activityName);
}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index d2172d3741d18e3c72c1b03c0a269f81e9678cbc..c3e9402a389e238cc93f4580ee58841769e02da2 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -20,7 +20,6 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -303,6 +302,7 @@ public class ApkLiteParseUtils {
int revisionCode = 0;
boolean coreApp = false;
boolean debuggable = false;
+ boolean profilableByShell = false;
boolean multiArch = false;
boolean use32bitAbi = false;
boolean extractNativeLibs = true;
@@ -379,6 +379,10 @@ public class ApkLiteParseUtils {
switch (attr) {
case "debuggable":
debuggable = attrs.getAttributeBooleanValue(i, false);
+ if (debuggable) {
+ // Debuggable implies profileable
+ profilableByShell = true;
+ }
break;
case "multiArch":
multiArch = attrs.getAttributeBooleanValue(i, false);
@@ -431,6 +435,13 @@ public class ApkLiteParseUtils {
minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
}
}
+ } else if (PackageParser.TAG_PROFILEABLE.equals(parser.getName())) {
+ for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+ final String attr = attrs.getAttributeName(i);
+ if ("shell".equals(attr)) {
+ profilableByShell = attrs.getAttributeBooleanValue(i, profilableByShell);
+ }
+ }
}
}
@@ -445,12 +456,13 @@ public class ApkLiteParseUtils {
overlayPriority = 0;
}
- return input.success(new PackageParser.ApkLite(codePath, packageSplit.first,
- packageSplit.second, isFeatureSplit, configForSplit, usesSplitName, isSplitRequired,
- versionCode, versionCodeMajor, revisionCode, installLocation, verifiers,
- signingDetails, coreApp, debuggable, multiArch, use32bitAbi, useEmbeddedDex,
- extractNativeLibs, isolatedSplits, targetPackage, overlayIsStatic, overlayPriority,
- minSdkVersion, targetSdkVersion));
+ return input.success(
+ new PackageParser.ApkLite(codePath, packageSplit.first, packageSplit.second,
+ isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode,
+ versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
+ coreApp, debuggable, profilableByShell, multiArch, use32bitAbi,
+ useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage,
+ overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion));
}
public static ParseResult> parsePackageSplitNames(ParseInput input,
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 317107829623e26c4d572da17e06057901512676..ab0ed51fb909f554a38974f89749ca2c88c6e52a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -1510,7 +1510,7 @@ public class ParsingPackageUtils {
Uri data = null;
String dataType = null;
- String host = IntentFilter.WILDCARD;
+ String host = null;
final int numActions = intentInfo.countActions();
final int numSchemes = intentInfo.countDataSchemes();
final int numTypes = intentInfo.countDataTypes();
@@ -2748,9 +2748,11 @@ public class ParsingPackageUtils {
SigningDetails verified;
try {
if (skipVerify) {
- // systemDir APKs are already trusted, save time by not verifying
+ // systemDir APKs are already trusted, save time by not verifying; since the
+ // signature is not verified and some system apps can have their V2+ signatures
+ // stripped allow pulling the certs from the jar signature.
verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
- baseCodePath, minSignatureScheme);
+ baseCodePath, SigningDetails.SignatureSchemeVersion.JAR);
} else {
verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index f64560a14832114606e2223e038cbaaeba9202f0..fb8fd74545c781e94ee72ba9f3942f2960d75635 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -302,7 +302,14 @@ public class ParsedActivityUtils {
}
String permission = array.getNonConfigurationString(permissionAttr, 0);
- activity.setPermission(permission != null ? permission : pkg.getPermission());
+ if (isAlias) {
+ // An alias will override permissions to allow referencing an Activity through its alias
+ // without needing the original permission. If an alias needs the same permission,
+ // it must be re-declared.
+ activity.setPermission(permission);
+ } else {
+ activity.setPermission(permission != null ? permission : pkg.getPermission());
+ }
final boolean setExported = array.hasValue(exportedAttr);
if (setExported) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index b37b617570533c04203f0fae9b6fbf6ffd08161a..6811e06fbe7e547e185babd91179d5953cad45cf 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -20,7 +20,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -29,9 +32,6 @@ import android.text.TextUtils;
import android.util.TypedValue;
import com.android.internal.annotations.VisibleForTesting;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
/** @hide */
class ParsedComponentUtils {
@@ -60,16 +60,27 @@ class ParsedComponentUtils {
component.setName(className);
component.setPackageName(packageName);
- if (useRoundIcon) {
- component.icon = array.getResourceId(roundIconAttr, 0);
+ int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0;
+ if (roundIconVal != 0) {
+ component.icon = roundIconVal;
+ component.nonLocalizedLabel = null;
+ } else {
+ int iconVal = array.getResourceId(iconAttr, 0);
+ if (iconVal != 0) {
+ component.icon = iconVal;
+ component.nonLocalizedLabel = null;
+ }
}
- if (component.icon == 0) {
- component.icon = array.getResourceId(iconAttr, 0);
+ int logoVal = array.getResourceId(logoAttr, 0);
+ if (logoVal != 0) {
+ component.logo = logoVal;
}
- component.logo = array.getResourceId(logoAttr, 0);
- component.banner = array.getResourceId(bannerAttr, 0);
+ int bannerVal = array.getResourceId(bannerAttr, 0);
+ if (bannerVal != 0) {
+ component.banner = bannerVal;
+ }
if (descriptionAttr != null) {
component.descriptionRes = array.getResourceId(descriptionAttr, 0);
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 8d472da1fb7cd0e4fad3f5d675da0a1a43e8affc..e385cd2b7ecdb88d2b92fe19caf7483afceae3dc 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -26,6 +26,8 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
import android.util.Slog;
/**
@@ -82,6 +84,9 @@ public class BiometricManager {
*
*
Types may combined via bitwise OR into a single integer representing multiple
* authenticators (e.g. DEVICE_CREDENTIAL | BIOMETRIC_WEAK).
+ *
+ * @see #canAuthenticate(int)
+ * @see BiometricPrompt.Builder#setAllowedAuthenticators(int)
*/
public interface Authenticators {
/**
@@ -116,22 +121,29 @@ public class BiometricManager {
/**
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
- * requirements for Strong, as defined by the Android CDD.
+ * requirements for Class 3 (formerly Strong), as defined
+ * by the Android CDD.
+ *
+ *
This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation.
+ *
+ * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
*/
int BIOMETRIC_STRONG = 0x000F;
/**
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
- * requirements for Weak, as defined by the Android CDD.
+ * requirements for Class 2 (formerly Weak), as defined by
+ * the Android CDD.
*
*
Note that this is a superset of {@link #BIOMETRIC_STRONG} and is defined such that
- * BIOMETRIC_STRONG | BIOMETRIC_WEAK == BIOMETRIC_WEAK.
+ * {@code BIOMETRIC_STRONG | BIOMETRIC_WEAK == BIOMETRIC_WEAK}.
*/
int BIOMETRIC_WEAK = 0x00FF;
/**
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
- * requirements for Convenience, as defined by the Android CDD.
+ * requirements for Class 1 (formerly Convenience), as
+ * defined by the Android CDD.
*
*
This constant is intended for use by {@link android.provider.DeviceConfig} to adjust
* the reported strength of a biometric sensor. It is not a valid parameter for any of the
@@ -153,6 +165,11 @@ public class BiometricManager {
* The non-biometric credential used to secure the device (i.e., PIN, pattern, or password).
* This should typically only be used in combination with a biometric auth type, such as
* {@link #BIOMETRIC_WEAK}.
+ *
+ *
This corresponds to {@link KeyProperties#AUTH_DEVICE_CREDENTIAL} during key
+ * generation.
+ *
+ * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
*/
int DEVICE_CREDENTIAL = 1 << 15;
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 5af7cef3e2b4862555e5a9214ee2f21d6e931a69..74caceae07c969569fcad17454823de357f916fb 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -36,6 +36,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.security.identity.IdentityCredential;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import android.util.Log;
@@ -371,6 +373,14 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* button on the prompt, making it an error to also call
* {@link #setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
*
+ *
If unlocking cryptographic operation(s), it is the application's responsibility to
+ * request authentication with the proper set of authenticators (e.g. match the
+ * authenticators specified during key generation).
+ *
+ * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+ * @see KeyProperties#AUTH_BIOMETRIC_STRONG
+ * @see KeyProperties#AUTH_DEVICE_CREDENTIAL
+ *
* @param authenticators A bit field representing all valid authenticator types that may be
* invoked by the prompt.
* @return This builder.
@@ -606,8 +616,24 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
- * A wrapper class for the crypto objects supported by BiometricPrompt. Currently the framework
- * supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ * A wrapper class for the cryptographic operations supported by BiometricPrompt.
+ *
+ *
Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, and
+ * {@link IdentityCredential}.
+ *
+ *
Cryptographic operations in Android can be split into two categories: auth-per-use and
+ * time-based. This is specified during key creation via the timeout parameter of the
+ * {@link KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)} API.
+ *
+ *
CryptoObjects are used to unlock auth-per-use keys via
+ * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)}, whereas time-based keys are unlocked for their specified duration
+ * any time the user authenticates with the specified authenticators (e.g. unlocking keyguard).
+ * If a time-based key is not available for use (i.e. none of the allowed authenticators have
+ * been unlocked recently), applications can prompt the user to authenticate via
+ * {@link BiometricPrompt#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
+ *
+ * @see Builder#setAllowedAuthenticators(int)
*/
public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
public CryptoObject(@NonNull Signature signature) {
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index b149d7798aab2cf7130ff872ebf3896428759c5e..dc56963ffd8c894a06c74d6fb0c1fd4d7911fea4 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -4086,10 +4086,11 @@ public final class CameraCharacteristics extends CameraMetadataThe accuracy of frame timestamp synchronization between physical cameras
*
The accuracy of the frame timestamp synchronization determines the physical cameras'
- * ability to start exposure at the same time. If the sensorSyncType is CALIBRATED,
- * the physical camera sensors usually run in master-slave mode so that their shutter
- * time is synchronized. For APPROXIMATE sensorSyncType, the camera sensors usually run in
- * master-master mode, and there could be offset between their start of exposure.
+ * ability to start exposure at the same time. If the sensorSyncType is CALIBRATED, the
+ * physical camera sensors usually run in leader/follower mode where one sensor generates a
+ * timing signal for the other, so that their shutter time is synchronized. For APPROXIMATE
+ * sensorSyncType, the camera sensors usually run in leader/leader mode, where both sensors
+ * use their own timing generator, and there could be offset between their start of exposure.
*
In both cases, all images generated for a particular capture request still carry the same
* timestamps, so that they can be used to look up the matching frame number and
* onCaptureStarted callback.
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 30ee32604939f0c2c1f1c8b0b8318498835eb6a8..15625cdeb8f4a7bd0ee4e4405c94946f89fca453 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -680,7 +680,7 @@ public abstract class CameraDevice implements AutoCloseable {
*
*
*
- *
Devices capable of streaming concurrently with other devices as described by
+ *
BACKWARD_COMPATIBLE devices capable of streaming concurrently with other devices as described by
* {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds} have the
* following guaranteed streams (when streaming concurrently with other devices)
*
@@ -696,10 +696,14 @@ public abstract class CameraDevice implements AutoCloseable {
*
*
*
+ *
Devices which are not backwards-compatible, support a mandatory single stream of size sVGA with image format {@code DEPTH16} during concurrent operation.
+ *
*
For guaranteed concurrent stream configurations:
- *
s720p refers to the camera device's resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ *
sVGA refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ * VGA resolution (640X480) whichever is lower.
+ *
s720p refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
* 720p(1280X720) whichever is lower.
- *
s1440p refers to the camera device's resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ *
s1440p refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
* 1440p(1920X1440) whichever is lower.
*
MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
* includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices
@@ -707,6 +711,7 @@ public abstract class CameraDevice implements AutoCloseable {
* streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level
* and capabilities.
*
+ *
*
Devices capable of outputting HEIC formats ({@link StreamConfigurationMap#getOutputFormats}
* contains {@link android.graphics.ImageFormat#HEIC}) will support substituting {@code JPEG}
* streams with {@code HEIC} in all guaranteed stream combinations for the device's hardware
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 6bbc37a90faee28615c3df2acb4fa2636bdaa5d7..7f834afd7b30075f08f8baab65f685c4685e4b6f 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -586,13 +586,27 @@ public final class CameraManager {
* priority when accessing the camera, and this method will succeed even if the camera device is
* in use by another camera API client. Any lower-priority application that loses control of the
* camera in this way will receive an
- * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.
+ * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.
+ * Opening the same camera ID twice in the same application will similarly cause the
+ * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback
+ * being fired for the {@link CameraDevice} from the first open call and all ongoing tasks
+ * being droppped.
*
*
Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
* be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
* for operation by calling {@link CameraDevice#createCaptureSession} and
* {@link CameraDevice#createCaptureRequest}
*
+ *
Before API level 30, when the application tries to open multiple {@link CameraDevice} of
+ * different IDs and the device does not support opening such combination, either the
+ * {@link #openCamera} will fail and throw a {@link CameraAccessException} or one or more of
+ * already opened {@link CameraDevice} will be disconnected and receive
+ * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback. Which
+ * behavior will happen depends on the device implementation and can vary on different devices.
+ * Starting in API level 30, if the device does not support the combination of cameras being
+ * opened, it is guaranteed the {@link #openCamera} call will fail and none of existing
+ * {@link CameraDevice} will be disconnected.
+ *
*
-
-
- 544dp
+ */
-
+package android.os;
+
+parcelable CarrierAssociatedAppEntry;
diff --git a/core/java/android/os/CarrierAssociatedAppEntry.java b/core/java/android/os/CarrierAssociatedAppEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..13f6eb63e29c86fc368292bd81474cb1005c6361
--- /dev/null
+++ b/core/java/android/os/CarrierAssociatedAppEntry.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+/**
+ * Represents a carrier app entry for use with {@link SystemConfigService}.
+ *
+ * @hide
+ */
+public final class CarrierAssociatedAppEntry implements Parcelable {
+
+ /**
+ * For carrier-associated app entries that don't specify the addedInSdk XML
+ * attribute.
+ */
+ public static final int SDK_UNSPECIFIED = -1;
+
+ public final String packageName;
+ /** May be {@link #SDK_UNSPECIFIED}. */
+ public final int addedInSdk;
+
+ public CarrierAssociatedAppEntry(String packageName, int addedInSdk) {
+ this.packageName = packageName;
+ this.addedInSdk = addedInSdk;
+ }
+
+ public CarrierAssociatedAppEntry(Parcel in) {
+ packageName = in.readString();
+ addedInSdk = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(packageName);
+ dest.writeInt(addedInSdk);
+ }
+
+ public static final Parcelable.Creator CREATOR =
+ new Parcelable.Creator() {
+ @Override
+ public CarrierAssociatedAppEntry createFromParcel(Parcel source) {
+ return new CarrierAssociatedAppEntry(source);
+ }
+
+ @Override
+ public CarrierAssociatedAppEntry[] newArray(int size) {
+ return new CarrierAssociatedAppEntry[size];
+ }
+ };
+}
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 034e6a7a06c4de822ff932a3c638ffa6c2de68a9..df58a6c636f5a92d80f53d8a6ac16a8e53212d5c 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -22,6 +22,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -93,8 +94,8 @@ public class GraphicsEnvironment {
private static final int GAME_DRIVER_GLOBAL_OPT_IN_OFF = 3;
private ClassLoader mClassLoader;
- private String mLayerPath;
- private String mDebugLayerPath;
+ private String mLibrarySearchPaths;
+ private String mLibraryPermittedPaths;
/**
* Set up GraphicsEnvironment
@@ -185,118 +186,131 @@ public class GraphicsEnvironment {
}
/**
- * Store the layer paths available to the loader.
+ * Store the class loader for namespace lookup later.
*/
public void setLayerPaths(ClassLoader classLoader,
- String layerPath,
- String debugLayerPath) {
+ String searchPaths,
+ String permittedPaths) {
// We have to store these in the class because they are set up before we
// have access to the Context to properly set up GraphicsEnvironment
mClassLoader = classLoader;
- mLayerPath = layerPath;
- mDebugLayerPath = debugLayerPath;
+ mLibrarySearchPaths = searchPaths;
+ mLibraryPermittedPaths = permittedPaths;
+ }
+
+ /**
+ * Returns the debug layer paths from settings.
+ * Returns null if:
+ * 1) The application process is not debuggable or layer injection metadata flag is not
+ * true; Or
+ * 2) ENABLE_GPU_DEBUG_LAYERS is not true; Or
+ * 3) Package name is not equal to GPU_DEBUG_APP.
+ */
+ public String getDebugLayerPathsFromSettings(
+ Bundle coreSettings, IPackageManager pm, String packageName,
+ ApplicationInfo ai) {
+ if (!debugLayerEnabled(coreSettings, packageName, ai)) {
+ return null;
+ }
+ Log.i(TAG, "GPU debug layers enabled for " + packageName);
+ String debugLayerPaths = "";
+
+ // Grab all debug layer apps and add to paths.
+ final String gpuDebugLayerApps =
+ coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP, "");
+ if (!gpuDebugLayerApps.isEmpty()) {
+ Log.i(TAG, "GPU debug layer apps: " + gpuDebugLayerApps);
+ // If a colon is present, treat this as multiple apps, so Vulkan and GLES
+ // layer apps can be provided at the same time.
+ final String[] layerApps = gpuDebugLayerApps.split(":");
+ for (int i = 0; i < layerApps.length; i++) {
+ String paths = getDebugLayerAppPaths(pm, layerApps[i]);
+ if (!paths.isEmpty()) {
+ // Append the path so files placed in the app's base directory will
+ // override the external path
+ debugLayerPaths += paths + File.pathSeparator;
+ }
+ }
+ }
+ return debugLayerPaths;
}
/**
* Return the debug layer app's on-disk and in-APK lib directories
*/
- private static String getDebugLayerAppPaths(PackageManager pm, String app) {
+ private static String getDebugLayerAppPaths(IPackageManager pm, String packageName) {
final ApplicationInfo appInfo;
try {
- appInfo = pm.getApplicationInfo(app, PackageManager.MATCH_ALL);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Debug layer app '" + app + "' not installed");
-
- return null;
+ appInfo = pm.getApplicationInfo(packageName, PackageManager.MATCH_ALL,
+ UserHandle.myUserId());
+ } catch (RemoteException e) {
+ return "";
+ }
+ if (appInfo == null) {
+ Log.w(TAG, "Debug layer app '" + packageName + "' not installed");
}
final String abi = chooseAbi(appInfo);
-
final StringBuilder sb = new StringBuilder();
sb.append(appInfo.nativeLibraryDir)
- .append(File.pathSeparator);
- sb.append(appInfo.sourceDir)
+ .append(File.pathSeparator)
+ .append(appInfo.sourceDir)
.append("!/lib/")
.append(abi);
final String paths = sb.toString();
-
if (DEBUG) Log.v(TAG, "Debug layer app libs: " + paths);
return paths;
}
+ private boolean debugLayerEnabled(Bundle coreSettings, String packageName, ApplicationInfo ai) {
+ // Only enable additional debug functionality if the following conditions are met:
+ // 1. App is debuggable or device is rooted or layer injection metadata flag is true
+ // 2. ENABLE_GPU_DEBUG_LAYERS is true
+ // 3. Package name is equal to GPU_DEBUG_APP
+ if (!isDebuggable() && !canInjectLayers(ai)) {
+ return false;
+ }
+ final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+ if (enable == 0) {
+ return false;
+ }
+ final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP, "");
+ if (packageName == null
+ || (gpuDebugApp.isEmpty() || packageName.isEmpty())
+ || !gpuDebugApp.equals(packageName)) {
+ return false;
+ }
+ return true;
+ }
+
/**
* Set up layer search paths for all apps
- * If debuggable, check for additional debug settings
*/
private void setupGpuLayers(
Context context, Bundle coreSettings, PackageManager pm, String packageName,
ApplicationInfo ai) {
+ final boolean enabled = debugLayerEnabled(coreSettings, packageName, ai);
String layerPaths = "";
+ if (enabled) {
+ layerPaths = mLibraryPermittedPaths;
- // Only enable additional debug functionality if the following conditions are met:
- // 1. App is debuggable or device is rooted or layer injection metadata flag is true
- // 2. ENABLE_GPU_DEBUG_LAYERS is true
- // 3. Package name is equal to GPU_DEBUG_APP
+ final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS);
+ Log.i(TAG, "Vulkan debug layer list: " + layers);
+ if (layers != null && !layers.isEmpty()) {
+ setDebugLayers(layers);
+ }
- if (isDebuggable() || canInjectLayers(ai)) {
-
- final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
-
- if (enable != 0) {
-
- final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP);
-
- if ((gpuDebugApp != null && packageName != null)
- && (!gpuDebugApp.isEmpty() && !packageName.isEmpty())
- && gpuDebugApp.equals(packageName)) {
- Log.i(TAG, "GPU debug layers enabled for " + packageName);
-
- // Prepend the debug layer path as a searchable path.
- // This will ensure debug layers added will take precedence over
- // the layers specified by the app.
- layerPaths = mDebugLayerPath + ":";
-
- // If there is a debug layer app specified, add its path.
- final String gpuDebugLayerApp =
- coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP);
-
- if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) {
- Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp);
- // If a colon is present, treat this as multiple apps, so Vulkan and GLES
- // layer apps can be provided at the same time.
- String[] layerApps = gpuDebugLayerApp.split(":");
- for (int i = 0; i < layerApps.length; i++) {
- String paths = getDebugLayerAppPaths(pm, layerApps[i]);
- if (paths != null) {
- // Append the path so files placed in the app's base directory will
- // override the external path
- layerPaths += paths + ":";
- }
- }
- }
-
- final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS);
-
- Log.i(TAG, "Vulkan debug layer list: " + layers);
- if (layers != null && !layers.isEmpty()) {
- setDebugLayers(layers);
- }
-
- final String layersGLES =
- coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES);
-
- Log.i(TAG, "GLES debug layer list: " + layersGLES);
- if (layersGLES != null && !layersGLES.isEmpty()) {
- setDebugLayersGLES(layersGLES);
- }
- }
+ final String layersGLES =
+ coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES);
+ Log.i(TAG, "GLES debug layer list: " + layersGLES);
+ if (layersGLES != null && !layersGLES.isEmpty()) {
+ setDebugLayersGLES(layersGLES);
}
}
// Include the app's lib directory in all cases
- layerPaths += mLayerPath;
-
+ layerPaths += mLibrarySearchPaths;
setLayerPaths(mClassLoader, layerPaths);
}
diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl
index d3b029854112fb25ec1da3c20cd4be36e94f06a8..52f0ce1f054f31d83480ef06cc71ab8102bb6a90 100644
--- a/core/java/android/os/ISystemConfig.aidl
+++ b/core/java/android/os/ISystemConfig.aidl
@@ -30,4 +30,9 @@ interface ISystemConfig {
* @see SystemConfigManager#getDisabledUntilUsedPreinstalledCarrierAssociatedApps
*/
Map getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+
+ /**
+ * @see SystemConfigManager#getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries
+ */
+ Map getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries();
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 5d2c9d18c00c20b81f889ccdc3517b32e016b6d0..a4077fbee892e9bd1f1d64c90c77252f468a0a63 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -228,6 +228,13 @@ public class Process {
*/
public static final int EXT_OBB_RW_GID = 1079;
+ /**
+ * GID that corresponds to the INTERNET permission.
+ * Must match the value of AID_INET.
+ * @hide
+ */
+ public static final int INET_GID = 3003;
+
/** {@hide} */
public static final int NOBODY_UID = 9999;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 02b822a99f2add052738fbf12fb629068d682d1b..257bc5b642857be7ad0456428a253a1d2a06d69d 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -84,6 +84,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
@@ -189,6 +191,9 @@ public final class StrictMode {
// Only show an annoying dialog at most every 30 seconds
private static final long MIN_DIALOG_INTERVAL_MS = 30000;
+ // Only log a dropbox entry at most every 30 seconds
+ private static final long MIN_DROPBOX_INTERVAL_MS = 3000;
+
// How many Span tags (e.g. animations) to report.
private static final int MAX_SPAN_TAGS = 20;
@@ -817,6 +822,9 @@ public final class StrictMode {
/** @hide */
public @NonNull Builder permitActivityLeaks() {
+ synchronized (StrictMode.class) {
+ sExpectedActivityInstanceCount.clear();
+ }
return disable(DETECT_VM_ACTIVITY_LEAKS);
}
@@ -1749,16 +1757,20 @@ public final class StrictMode {
// Not perfect, but fast and good enough for dup suppression.
Integer crashFingerprint = info.hashCode();
long lastViolationTime = 0;
- if (mLastViolationTime != null) {
- Long vtime = mLastViolationTime.get(crashFingerprint);
- if (vtime != null) {
- lastViolationTime = vtime;
+ long now = SystemClock.uptimeMillis();
+ if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
+ if (mLastViolationTime != null) {
+ Long vtime = mLastViolationTime.get(crashFingerprint);
+ if (vtime != null) {
+ lastViolationTime = vtime;
+ }
+ clampViolationTimeMap(mLastViolationTime, Math.max(MIN_LOG_INTERVAL_MS,
+ Math.max(MIN_DIALOG_INTERVAL_MS, MIN_DROPBOX_INTERVAL_MS)));
+ } else {
+ mLastViolationTime = new ArrayMap<>(1);
}
- } else {
- mLastViolationTime = new ArrayMap<>(1);
+ mLastViolationTime.put(crashFingerprint, now);
}
- long now = SystemClock.uptimeMillis();
- mLastViolationTime.put(crashFingerprint, now);
long timeSinceLastViolationMillis =
lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime);
@@ -1777,7 +1789,8 @@ public final class StrictMode {
penaltyMask |= PENALTY_DIALOG;
}
- if (info.penaltyEnabled(PENALTY_DROPBOX) && lastViolationTime == 0) {
+ if (info.penaltyEnabled(PENALTY_DROPBOX)
+ && timeSinceLastViolationMillis > MIN_DROPBOX_INTERVAL_MS) {
penaltyMask |= PENALTY_DROPBOX;
}
@@ -1905,9 +1918,16 @@ public final class StrictMode {
}
private static class AndroidCloseGuardReporter implements CloseGuard.Reporter {
+
+ @Override
public void report(String message, Throwable allocationSite) {
onVmPolicyViolation(new LeakedClosableViolation(message, allocationSite));
}
+
+ @Override
+ public void report(String message) {
+ onVmPolicyViolation(new LeakedClosableViolation(message));
+ }
}
/** Called from Parcel.writeNoException() */
@@ -2212,6 +2232,23 @@ public final class StrictMode {
@UnsupportedAppUsage
private static final HashMap sLastVmViolationTime = new HashMap<>();
+ /**
+ * Clamp the given map by removing elements with timestamp older than the given retainSince.
+ */
+ private static void clampViolationTimeMap(final @NonNull Map violationTime,
+ final long retainSince) {
+ final Iterator> iterator = violationTime.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry e = iterator.next();
+ if (e.getValue() < retainSince) {
+ // Remove stale entries
+ iterator.remove();
+ }
+ }
+ // Ideally we'd cap the total size of the map, though it'll involve quickselect of topK,
+ // seems not worth it (saving some space immediately but they will be obsoleted soon anyway)
+ }
+
/** @hide */
public static void onVmPolicyViolation(Violation originStack) {
onVmPolicyViolation(originStack, false);
@@ -2235,13 +2272,17 @@ public final class StrictMode {
final long now = SystemClock.uptimeMillis();
long lastViolationTime;
long timeSinceLastViolationMillis = Long.MAX_VALUE;
- synchronized (sLastVmViolationTime) {
- if (sLastVmViolationTime.containsKey(fingerprint)) {
- lastViolationTime = sLastVmViolationTime.get(fingerprint);
- timeSinceLastViolationMillis = now - lastViolationTime;
- }
- if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
- sLastVmViolationTime.put(fingerprint, now);
+ if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
+ synchronized (sLastVmViolationTime) {
+ if (sLastVmViolationTime.containsKey(fingerprint)) {
+ lastViolationTime = sLastVmViolationTime.get(fingerprint);
+ timeSinceLastViolationMillis = now - lastViolationTime;
+ }
+ if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
+ sLastVmViolationTime.put(fingerprint, now);
+ }
+ clampViolationTimeMap(sLastVmViolationTime,
+ now - Math.max(MIN_VM_INTERVAL_MS, MIN_LOG_INTERVAL_MS));
}
}
if (timeSinceLastViolationMillis <= MIN_VM_INTERVAL_MS) {
@@ -2586,8 +2627,10 @@ public final class StrictMode {
return;
}
+ // Use the instance count from InstanceTracker as initial value.
Integer expected = sExpectedActivityInstanceCount.get(klass);
- Integer newExpected = expected == null ? 1 : expected + 1;
+ Integer newExpected =
+ expected == null ? InstanceTracker.getInstanceCount(klass) + 1 : expected + 1;
sExpectedActivityInstanceCount.put(klass, newExpected);
}
}
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index 3a9ce2fa85f13ba50c5c2c1a21f25263590a03c0..12a1ffaf69c15a850a184129a409c1ed327aa1fa 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -88,4 +88,29 @@ public class SystemConfigManager {
return Collections.emptyMap();
}
}
+
+ /**
+ * Returns a map that describes helper apps associated with carrier apps that, like the apps
+ * returned by {@link #getDisabledUntilUsedPreinstalledCarrierApps()}, should be disabled until
+ * the correct SIM is inserted into the device.
+ *
+ *
TODO(b/159069037) expose this and get rid of the other method that omits SDK version.
+ *
+ * @return A map with keys corresponding to package names returned by
+ * {@link #getDisabledUntilUsedPreinstalledCarrierApps()} and values as lists of package
+ * names of helper apps and the SDK versions when they were first added.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.READ_CARRIER_APP_INFO)
+ public @NonNull Map>
+ getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries() {
+ try {
+ return (Map>)
+ mInterface.getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Caught remote exception", e);
+ return Collections.emptyMap();
+ }
+ }
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7845200f4bf7ceac5dfd096b66396a5217789182..2465b0e418766501f92d2251a7db8991f03ae0a4 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2699,15 +2699,12 @@ public class UserManager {
* @param name the user's name
* @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
* @param flags UserInfo flags that specify user properties.
- * @return the {@link UserInfo} object for the created user,
- * or throws {@link UserOperationException} if the user could not be created
- * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
- * (otherwise returns {@code null}).
+ * @return the {@link UserInfo} object for the created user, or {@code null} if the user
+ * could not be created.
*
- * @throws UserOperationException if the user could not be created and the calling app is
- * targeting {@link android.os.Build.VERSION_CODES#R} or above.
- * @hide
* @see UserInfo
+ *
+ * @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
@@ -2716,8 +2713,7 @@ public class UserManager {
try {
return mService.createUserWithThrow(name, userType, flags);
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ return null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2743,25 +2739,19 @@ public class UserManager {
* com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION}.
*
* @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
- * @return the {@link UserInfo} object for the created user,
- * or throws {@link UserOperationException} if the user could not be created
- * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
- * (otherwise returns {@code null}).
- *
- * @throws UserOperationException if the user could not be created and the calling app is
- * targeting {@link android.os.Build.VERSION_CODES#R} or above.
+ * @return the {@link UserInfo} object for the created user.
*
+ * @throws UserOperationException if the user could not be created.
* @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
- public @Nullable UserInfo preCreateUser(@NonNull String userType)
+ public @NonNull UserInfo preCreateUser(@NonNull String userType)
throws UserOperationException {
try {
return mService.preCreateUserWithThrow(userType);
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ throw UserOperationException.from(e);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2771,18 +2761,14 @@ public class UserManager {
* Creates a guest user and configures it.
* @param context an application context
* @param name the name to set for the user
- * @return the {@link UserInfo} object for the created user,
- * or throws {@link UserOperationException} if the user could not be created
- * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
- * (otherwise returns {@code null}).
+ * @return the {@link UserInfo} object for the created user, or {@code null} if the user
+ * could not be created.
*
- * @throws UserOperationException if the user could not be created and the calling app is
- * targeting {@link android.os.Build.VERSION_CODES#R} or above.
* @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
- public UserInfo createGuest(Context context, String name) throws UserOperationException {
+ public UserInfo createGuest(Context context, String name) {
UserInfo guest = null;
try {
guest = mService.createUserWithThrow(name, USER_TYPE_FULL_GUEST, 0);
@@ -2791,8 +2777,7 @@ public class UserManager {
Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
}
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ return null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2902,26 +2887,20 @@ public class UserManager {
* @param userId new user will be a profile of this user.
* @param disallowedPackages packages that will not be installed in the profile being created.
*
- * @return the {@link UserInfo} object for the created user,
- * or throws {@link UserOperationException} if the user could not be created
- * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
- * (otherwise returns {@code null}).
+ * @return the {@link UserInfo} object for the created user, or {@code null} if the user could
+ * not be created.
*
- * @throws UserOperationException if the user could not be created and the calling app is
- * targeting {@link android.os.Build.VERSION_CODES#R} or above.
* @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
public UserInfo createProfileForUser(String name, @NonNull String userType,
- @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages)
- throws UserOperationException {
+ @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages) {
try {
return mService.createProfileForUserWithThrow(name, userType, flags, userId,
disallowedPackages);
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ return null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2938,13 +2917,12 @@ public class UserManager {
Manifest.permission.CREATE_USERS})
public UserInfo createProfileForUserEvenWhenDisallowed(String name,
@NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId,
- String[] disallowedPackages) throws UserOperationException {
+ String[] disallowedPackages) {
try {
return mService.createProfileForUserEvenWhenDisallowedWithThrow(name, userType, flags,
userId, disallowedPackages);
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ return null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2955,18 +2933,14 @@ public class UserManager {
* restrictions and adds shared accounts.
*
* @param name profile's name
- * @return the {@link UserInfo} object for the created user,
- * or throws {@link UserOperationException} if the user could not be created
- * and calling app is targeting {@link android.os.Build.VERSION_CODES#R} or above
- * (otherwise returns {@code null}).
+ * @return the {@link UserInfo} object for the created user, or {@code null} if the user
+ * could not be created.
*
- * @throws UserOperationException if the user could not be created and the calling app is
- * targeting {@link android.os.Build.VERSION_CODES#R} or above.
* @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
- public UserInfo createRestrictedProfile(String name) throws UserOperationException {
+ public UserInfo createRestrictedProfile(String name) {
try {
UserHandle parentUserHandle = Process.myUserHandle();
UserInfo user = mService.createRestrictedProfileWithThrow(name,
@@ -2977,8 +2951,7 @@ public class UserManager {
}
return user;
} catch (ServiceSpecificException e) {
- return returnNullOrThrowUserOperationException(e,
- mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
+ return null;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -4009,15 +3982,15 @@ public class UserManager {
* Sets the user's photo.
* @param userId the user for whom to change the photo.
* @param icon the bitmap to set as the photo.
+ *
* @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
- public void setUserIcon(@UserIdInt int userId, @NonNull Bitmap icon)
- throws UserOperationException {
+ public void setUserIcon(@UserIdInt int userId, @NonNull Bitmap icon) {
try {
mService.setUserIcon(userId, icon);
} catch (ServiceSpecificException e) {
- throw UserOperationException.from(e);
+ return;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -4027,6 +4000,10 @@ public class UserManager {
* Sets the context user's photo.
*
* @param icon the bitmap to set as the photo.
+ *
+ * @throws UserOperationException according to the function signature, but may not actually
+ * throw it in practice. Catch RuntimeException instead.
+ *
* @hide
*/
@SystemApi
@@ -4090,26 +4067,19 @@ public class UserManager {
public static int getMaxSupportedUsers() {
// Don't allow multiple users on certain builds
if (android.os.Build.ID.startsWith("JVP")) return 1;
- if (ActivityManager.isLowRamDeviceStatic()) {
- // Low-ram devices are Svelte. Most of the time they don't get multi-user.
- if ((Resources.getSystem().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
- != Configuration.UI_MODE_TYPE_TELEVISION) {
- return 1;
- }
- }
return SystemProperties.getInt("fw.max_users",
Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers));
}
/**
- * Returns true if the user switcher should be shown.
- * I.e., returns whether the user switcher is enabled and there is something actionable to show.
+ * Returns true if the user switcher is enabled (regardless of whether there is anything
+ * interesting for it to show).
*
- * @return true if user switcher should be shown.
+ * @return true if user switcher is enabled
* @hide
*/
public boolean isUserSwitcherEnabled() {
- return isUserSwitcherEnabled(false);
+ return isUserSwitcherEnabled(true);
}
/**
diff --git a/core/java/android/os/Users.md b/core/java/android/os/Users.md
index 3bbbe5452fd3612c515e06b175820a2c61ab1572..b019b0dc178be989a04c6c77bca10478443639fa 100644
--- a/core/java/android/os/Users.md
+++ b/core/java/android/os/Users.md
@@ -18,54 +18,80 @@
## Concepts
-### User
+### Users and profiles
-A user of a device e.g. usually a human being. Each user has its own home screen.
+#### User
-#### User Profile
+A user is a representation of a person using a device, with their own distinct application data
+and some unique settings. Throughout this document, the word 'user' will be used in this technical
+sense, i.e. for this virtual environment, whereas the word 'person' will be used to denote an actual
+human interacting with the device.
-A user can have multiple profiles. E.g. one for the private life and one for work. Each profile
-has a different set of apps and accounts but they share one home screen. All profiles of a
-profile group can be active at the same time.
-
-Each profile has a separate [`userId`](#int-userid). Unless needed user profiles are treated as
-completely separate users.
+Each user has a separate [`userId`](#int-userid).
#### Profile Group
-All user profiles that share a home screen. You can list the profiles of a user via
-`UserManager#getEnabledProfiles` (you usually don't deal with disabled profiles)
+Often, there is a 1-to-1 mapping of people who use a device to 'users'; e.g. there may be two users
+on a device - the owner and a guest, each with their own separate home screen.
-#### Foreground user vs background user
+However, Android also supports multiple profiles for a single person, e.g. one for their private
+life and one for work, both sharing a single home screen.
+Each profile in a profile group is a distinct user, with a unique [`userId`](#int-userid), and have
+a different set of apps and accounts,
+but they share a single UI, single launcher, and single wallpaper.
+All profiles of a profile group can be active at the same time.
-Only a single user profile group can be in the foreground. This is the user profile the user
-currently interacts with.
+You can list the profiles of a user via `UserManager#getEnabledProfiles` (you usually don't deal
+with disabled profiles)
-#### Parent user (profile)
+#### Parent user
-The main profile of a profile group, usually the personal (as opposed to work) profile. Get this via
-`UserManager#getProfileParent` (returns `null` if the user does not have profiles)
+The main user of a profile group, to which the other profiles of the group 'belong'.
+This is usually the personal (as opposed to work) profile. Get this via
+`UserManager#getProfileParent` (returns `null` if the user does not have profiles).
-#### Managed user (profile)
+#### Profile (Managed profile)
-The other profiles of a profile group. The name comes from the fact that these profiles are usually
+A profile of the parent user, i.e. a profile belonging to the same profile group as a parent user,
+with whom they share a single home screen.
+Currently, the only type of profile supported in AOSP is a 'Managed Profile'.
+The name comes from the fact that these profiles are usually
managed by a device policy controller app. You can create a managed profile from within the device
policy controller app on your phone.
+Note that, as a member of the profile group, the parent user may sometimes also be considered a
+'profile', but generally speaking, the word 'profile' denotes a user that is subordinate to a
+parent.
+
+#### Foreground user vs background user
+
+Only a single user can be in the foreground.
+This is the user with whom the person using the device is currently interacting, or, in the case
+of profiles, the parent profile of this user.
+All other running users are background users.
+Some users may not be running at all, neither in the foreground nor the background.
+
#### Account
-An account of a user profile with a (usually internet based) service. E.g. aname@gmail.com or
-aname@yahoo.com. Each profile can have multiple accounts. A profile does not have to have a
+An account of a user with a (usually internet based) service. E.g. aname@gmail.com or
+aname@yahoo.com. Each user can have multiple accounts. A user does not have to have a
account.
+#### System User
+
+The user with [`userId`](#int-userid) 0 denotes the system user, which is always required to be
+running.
+
+On most devices, the system user is also used by the primary person using the device; however,
+on certain types of devices, the system user may be a stand-alone user, not intended for direct
+human interaction.
+
## Data types
### int userId
-... usually marked as `@UserIdInt`
-
-The id of a user profile. List all users via `adb shell dumpsys user`. There is no data type for a
-user, all you can do is using the user id of the parent profile as a proxy for the user.
+The id of a user. List all users via `adb shell dumpsys user`.
+In code, these are sometimes marked as `@UserIdInt`.
### int uid
@@ -97,10 +123,10 @@ mechanism should be access controlled by permissions.
A system service should deal with users being started and stopped by overriding
`SystemService.onSwitchUser` and `SystemService.onStopUser`.
-If users profiles become inactive the system should stop all apps of this profile from interacting
+If a user become inactive the system should stop all apps of this user from interacting
with other apps or the system.
-Another important lifecycle event is `onUnlockUser`. Only for unlocked user profiles you can access
+Another important lifecycle event is `onUnlockUser`. Only for an unlocked user can you access
all data, e.g. which packages are installed.
You only want to deal with user profiles that
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index fd7cdda3808a436cdc3dd4fa59e54ff8719dc0a9..06254574401b70e8236e668a4f95769d25b4db59 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -221,15 +221,16 @@ public abstract class VibrationEffect implements Parcelable {
*
* Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
* each pair, the value in the amplitude array determines the strength of the vibration and the
- * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no
- * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored.
+ * value in the timing array determines how long it vibrates for, in milliseconds. Amplitude
+ * values must be between 0 and 255, and an amplitude of 0 implies no vibration (i.e. off). Any
+ * pairs with a timing value of 0 will be ignored.
*
* To cause the pattern to repeat, pass the index into the timings array at which to start the
* repetition, or -1 to disable repeating.
*
*
- * @param timings The timing values of the timing / amplitude pairs. Timing values of 0
- * will cause the pair to be ignored.
+ * @param timings The timing values, in milliseconds, of the timing / amplitude pairs. Timing
+ * values of 0 will cause the pair to be ignored.
* @param amplitudes The amplitude values of the timing / amplitude pairs. Amplitude values
* must be between 0 and 255, or equal to {@link #DEFAULT_AMPLITUDE}. An
* amplitude value of 0 implies the motor is off.
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index a4c99c006d8089dc17a5410a6b1048b6b2b21dbd..39038f555044e038a1ef7c450f31801fbed4b4b7 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -1318,15 +1318,15 @@ public class ZygoteProcess {
Process.ProcessStartResult result;
try {
- // As app zygote is for generating isolated process, at the end it can't access
- // apps data, so doesn't need to its data info.
+ // We will bind mount app data dirs so app zygote can't access /data/data, while
+ // we don't need to bind mount storage dirs as /storage won't be mounted.
result = startViaZygote(processClass, niceName, uid, gid,
gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
true /* startChildZygote */, null /* packageName */,
ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
- null /* whitelistedDataInfoMap */, false /* bindMountAppsData*/,
+ null /* whitelistedDataInfoMap */, true /* bindMountAppsData*/,
/* bindMountAppStorageDirs */ false, extraArgs);
} catch (ZygoteStartFailedEx ex) {
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 220ce22ded5c1d13f988746179c321529107b2ae..61e6a05fce3700375790d88545516b0faa837463 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -110,6 +110,11 @@ interface IIncrementalService {
*/
void deleteStorage(int storageId);
+ /**
+ * Permanently disable readlogs reporting for a storage given its ID.
+ */
+ void disableReadLogs(int storageId);
+
/**
* Setting up native library directories and extract native libs onto a storage if needed.
*/
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 863d86ef88c9b5cfbae820ea2e9141b56f59f0c4..31ccf95ba16f7b364ebab2c0063410e6cfa44a6b 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -152,6 +152,13 @@ public final class IncrementalFileStorages {
}
}
+ /**
+ * Permanently disables readlogs.
+ */
+ public void disableReadLogs() {
+ mDefaultStorage.disableReadLogs();
+ }
+
/**
* Resets the states and unbinds storage instances for an installation session.
* TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 6200a38fe13c61030c28852ac338449f5df4c474..ca6114f29b9cb2204e1727ec42d1c4ee0504e45e 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -417,6 +417,17 @@ public final class IncrementalStorage {
private static final int INCFS_MAX_HASH_SIZE = 32; // SHA256
private static final int INCFS_MAX_ADD_DATA_SIZE = 128;
+ /**
+ * Permanently disable readlogs collection.
+ */
+ public void disableReadLogs() {
+ try {
+ mService.disableReadLogs(mId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Deserialize and validate v4 signature bytes.
*/
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4ca48cb3e57cccd86e1da029d263daaed349cffb..0abf8ae352afa8b128692ddcd9b2adedfdbd3bb8 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -16,9 +16,11 @@
package android.os.storage;
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
+import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_READ_MEDIA_AUDIO;
import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES;
@@ -1365,6 +1367,7 @@ public class StorageManager {
String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid(
android.os.Process.myUid());
if (packageNames == null || packageNames.length <= 0) {
+ Log.w(TAG, "Missing package names; no storage volumes available");
return new StorageVolume[0];
}
packageName = packageNames[0];
@@ -1372,6 +1375,7 @@ public class StorageManager {
final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
if (uid <= 0) {
+ Log.w(TAG, "Missing UID; no storage volumes available");
return new StorageVolume[0];
}
return storageManager.getVolumeList(uid, packageName, flags);
@@ -1851,7 +1855,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionReadAudio(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
return false;
}
@@ -1862,7 +1866,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionWriteAudio(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
return false;
}
@@ -1873,7 +1877,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionReadVideo(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
return false;
}
@@ -1884,7 +1888,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionWriteVideo(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
return false;
}
@@ -1895,7 +1899,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionReadImages(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
return false;
}
@@ -1906,7 +1910,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionWriteImages(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
return false;
}
@@ -1914,6 +1918,24 @@ public class StorageManager {
OP_WRITE_MEDIA_IMAGES);
}
+ private boolean checkExternalStoragePermissionAndAppOp(boolean enforce,
+ int pid, int uid, String packageName, @Nullable String featureId, String permission,
+ int op) {
+ // First check if app has MANAGE_EXTERNAL_STORAGE.
+ final int mode = mAppOps.noteOpNoThrow(OP_MANAGE_EXTERNAL_STORAGE, uid, packageName,
+ featureId, null);
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
+ if (mode == AppOpsManager.MODE_DEFAULT && mContext.checkPermission(
+ MANAGE_EXTERNAL_STORAGE, pid, uid) == PERMISSION_GRANTED) {
+ return true;
+ }
+ // If app doesn't have MANAGE_EXTERNAL_STORAGE, then check if it has requested granular
+ // permission.
+ return checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, permission, op);
+ }
+
/** {@hide} */
@VisibleForTesting
public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
diff --git a/core/java/android/os/strictmode/LeakedClosableViolation.java b/core/java/android/os/strictmode/LeakedClosableViolation.java
index c795a6b89ec01305532df4c91b26695f347aa5fd..a2b02833afa0bccf3aabebc426a22e91745fd732 100644
--- a/core/java/android/os/strictmode/LeakedClosableViolation.java
+++ b/core/java/android/os/strictmode/LeakedClosableViolation.java
@@ -21,4 +21,9 @@ public final class LeakedClosableViolation extends Violation {
super(message);
initCause(allocationSite);
}
+
+ /** @hide */
+ public LeakedClosableViolation(String message) {
+ super(message);
+ }
}
diff --git a/core/java/android/os/strictmode/Violation.java b/core/java/android/os/strictmode/Violation.java
index 31c7d584fd6548c3caecc606e0652c91bf3d08b0..0edb78a6424333f74dab4fc681e94f76bf3f2876 100644
--- a/core/java/android/os/strictmode/Violation.java
+++ b/core/java/android/os/strictmode/Violation.java
@@ -18,7 +18,58 @@ package android.os.strictmode;
/** Root class for all StrictMode violations. */
public abstract class Violation extends Throwable {
+ private int mHashCode;
+ private boolean mHashCodeValid;
+
Violation(String message) {
super(message);
}
+
+ @Override
+ public int hashCode() {
+ synchronized (this) {
+ if (mHashCodeValid) {
+ return mHashCode;
+ }
+ final String message = getMessage();
+ final Throwable cause = getCause();
+ int hashCode = message != null ? message.hashCode() : getClass().hashCode();
+ hashCode = hashCode * 37 + calcStackTraceHashCode(getStackTrace());
+ hashCode = hashCode * 37 + (cause != null ? cause.toString().hashCode() : 0);
+ mHashCodeValid = true;
+ return mHashCode = hashCode;
+ }
+ }
+
+ @Override
+ public synchronized Throwable initCause(Throwable cause) {
+ mHashCodeValid = false;
+ return super.initCause(cause);
+ }
+
+ @Override
+ public void setStackTrace(StackTraceElement[] stackTrace) {
+ super.setStackTrace(stackTrace);
+ synchronized (this) {
+ mHashCodeValid = false;
+ }
+ }
+
+ @Override
+ public synchronized Throwable fillInStackTrace() {
+ mHashCodeValid = false;
+ return super.fillInStackTrace();
+ }
+
+ private static int calcStackTraceHashCode(final StackTraceElement[] stackTrace) {
+ int hashCode = 17;
+ if (stackTrace != null) {
+ for (int i = 0; i < stackTrace.length; i++) {
+ if (stackTrace[i] != null) {
+ hashCode = hashCode * 37 + stackTrace[i].hashCode();
+ }
+ }
+ }
+ return hashCode;
+ }
}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 235b0830b9aaa4cc80d74949c9493487a21ab122..e23102113e9f1f374c339ebc107e91bfa2bd05ff 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -71,7 +71,7 @@ interface IPermissionManager {
void grantRuntimePermission(String packageName, String permName, int userId);
- void revokeRuntimePermission(String packageName, String permName, int userId);
+ void revokeRuntimePermission(String packageName, String permName, int userId, String reason);
void resetRuntimePermissions();
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
index 2bf08e2ff2d43aa30ee9bf6323562eae528ac0a7..1ef3ad211cee96b75303050f60ab140d9718b2e5 100644
--- a/core/java/android/permission/Permissions.md
+++ b/core/java/android/permission/Permissions.md
@@ -706,9 +706,9 @@ App-op permissions are user-switchable permissions that are not runtime permissi
be used for permissions that are really only meant to be ever granted to a very small amount of
apps. Traditionally granting these permissions is intentionally very heavy weight so that the
user really needs to understand the use case. For example one use case is the
-`INTERACT_ACROSS_PROFILES` permission that allows apps of different
-[user profiles](../os/Users.md#user-profile) to interact. Of course this is breaking a very basic
-security container and hence should only every be granted with a lot of care.
+`INTERACT_ACROSS_PROFILES` permission that allows apps of different users within the same
+[profile group](../os/Users.md#profile-group) to interact. Of course this is breaking a very basic
+security container and hence should only ever be granted with a lot of care.
**Warning:** Most app-op permissions follow this logic, but most of them also have exceptions
and special behavior. Hence this section is a guideline, not a rule.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1b19e1290121b749d0573cb952f148a321374939..64d9c9dcc6e0f2584e716bde437931463b95a916 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
@@ -1777,6 +1778,15 @@ public final class Settings {
public static final String ACTION_NOTIFICATION_SETTINGS
= "android.settings.NOTIFICATION_SETTINGS";
+ /**
+ * Activity Action: Show conversation settings.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CONVERSATION_SETTINGS
+ = "android.settings.CONVERSATION_SETTINGS";
+
/**
* Activity Action: Show notification history screen.
*
@@ -1887,6 +1897,15 @@ public final class Settings {
public static final String ACTION_DEVICE_CONTROLS_SETTINGS =
"android.settings.ACTION_DEVICE_CONTROLS_SETTINGS";
+ /**
+ * Activity Action: Show media control settings
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MEDIA_CONTROLS_SETTINGS =
+ "android.settings.ACTION_MEDIA_CONTROLS_SETTINGS";
+
/**
* Activity Action: Show a dialog with disabled by policy message.
*
If an user action is disabled by policy, this dialog can be triggered to let
@@ -1962,6 +1981,10 @@ public final class Settings {
* Input: Nothing.
*
* Output: Nothing.
+ *
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_WEBVIEW_SETTINGS = "android.settings.WEBVIEW_SETTINGS";
@@ -6229,6 +6252,8 @@ public final class Settings {
* determines if the IME should be shown when a hard keyboard is attached.
* @hide
*/
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
/**
@@ -8505,14 +8530,15 @@ public final class Settings {
public static final int VR_DISPLAY_MODE_OFF = 1;
/**
- * Whether CarrierAppUtils#disableCarrierAppsUntilPrivileged has been executed at least
- * once.
+ * The latest SDK version that CarrierAppUtils#disableCarrierAppsUntilPrivileged has been
+ * executed for.
*
*
This is used to ensure that we only take one pass which will disable apps that are not
* privileged (if any). From then on, we only want to enable apps (when a matching SIM is
* inserted), to avoid disabling an app that the user might actively be using.
*
- *
Will be set to 1 once executed.
+ *
Will be set to {@link android.os.Build.VERSION#SDK_INT} once executed. Note that older
+ * SDK versions prior to R set 1 for this value.
*
* @hide
*/
@@ -8893,6 +8919,15 @@ public final class Settings {
*/
public static final String PEOPLE_STRIP = "people_strip";
+ /**
+ * Whether or not to enable media resumption
+ * When enabled, media controls in quick settings will populate on boot and persist if
+ * resumable via a MediaBrowserService.
+ * @see Settings.Global#SHOW_MEDIA_ON_QUICK_SETTINGS
+ * @hide
+ */
+ public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption";
+
/**
* Controls if window magnification is enabled.
* @hide
@@ -11974,7 +12009,7 @@ public final class Settings {
* @see #ENABLE_RESTRICTED_BUCKET
* @hide
*/
- public static final int DEFAULT_ENABLE_RESTRICTED_BUCKET = 1;
+ public static final int DEFAULT_ENABLE_RESTRICTED_BUCKET = 0;
/**
* Whether or not app auto restriction is enabled. When it is enabled, settings app will
@@ -14244,15 +14279,6 @@ public final class Settings {
*/
public static final String KERNEL_CPU_THREAD_READER = "kernel_cpu_thread_reader";
- /**
- * Persistent user id that is last logged in to.
- *
- * They map to user ids, for example, 10, 11, 12.
- *
- * @hide
- */
- public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id";
-
/**
* Whether we've enabled native flags health check on this device. Takes effect on
* reboot. The value "1" enables native flags health check; otherwise it's disabled.
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index b34268d04238fe9fc714e52a6accbba57d031e09..a2489b9b68d9d15944f78c0f073e16ef804686bc 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4324,6 +4324,15 @@ public final class Telephony {
*/
public static final String ETWS_WARNING_TYPE = "etws_warning_type";
+ /**
+ * ETWS (Earthquake and Tsunami Warning System) primary message or not (ETWS alerts only).
+ *
See {@link android.telephony.SmsCbEtwsInfo}
+ *
Type: BOOLEAN
+ *
+ * @hide // TODO: Unhide this for S.
+ */
+ public static final String ETWS_IS_PRIMARY = "etws_is_primary";
+
/**
* CMAS (Commercial Mobile Alert System) message class (CMAS alerts only).
*
See {@link android.telephony.SmsCbCmasInfo}
@@ -4464,37 +4473,6 @@ public final class Telephony {
CMAS_URGENCY,
CMAS_CERTAINTY
};
-
- /**
- * Query columns for instantiating {@link android.telephony.SmsCbMessage} objects.
- * @hide
- */
- public static final String[] QUERY_COLUMNS_FWK = {
- _ID,
- SLOT_INDEX,
- SUBSCRIPTION_ID,
- GEOGRAPHICAL_SCOPE,
- PLMN,
- LAC,
- CID,
- SERIAL_NUMBER,
- SERVICE_CATEGORY,
- LANGUAGE_CODE,
- MESSAGE_BODY,
- MESSAGE_FORMAT,
- MESSAGE_PRIORITY,
- ETWS_WARNING_TYPE,
- CMAS_MESSAGE_CLASS,
- CMAS_CATEGORY,
- CMAS_RESPONSE_TYPE,
- CMAS_SEVERITY,
- CMAS_URGENCY,
- CMAS_CERTAINTY,
- RECEIVED_TIME,
- MESSAGE_BROADCASTED,
- GEOMETRIES,
- MAXIMUM_WAIT_TIME
- };
}
/**
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 08aa534be15276364f63e8a762dc0abb1043c48a..2d99c413cc89d8b445d522e7c606de186f4c33e7 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -312,7 +312,12 @@ public final class Dataset implements Parcelable {
* setting it to the {@link
* android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra. If you
* provide a dataset in the result, it will replace the authenticated dataset and
- * will be immediately filled in. If you provide a response, it will replace the
+ * will be immediately filled in. An exception to this behavior is if the original
+ * dataset represents a pinned inline suggestion (i.e. any of the field in the dataset
+ * has a pinned inline presentation, see {@link InlinePresentation#isPinned()}), then
+ * the original dataset will not be replaced,
+ * so that it can be triggered as a pending intent again.
+ * If you provide a response, it will replace the
* current response and the UI will be refreshed. For example, if you provided
* credit card information without the CVV for the data set in the {@link FillResponse
* response} then the returned data set should contain the CVV entry.
diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
index bf0bb9e2a41f54eefd52e7da7776177df2c0220a..7cd372fe97d8426cef6a3de439b89cccda1c7c81 100644
--- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
@@ -29,6 +29,12 @@ import android.service.autofill.InlinePresentation;
oneway interface IInlineSuggestionRenderService {
void renderSuggestion(in IInlineSuggestionUiCallback callback,
in InlinePresentation presentation, int width, int height,
- in IBinder hostInputToken, int displayId);
+ in IBinder hostInputToken, int displayId, int userId, int sessionId);
void getInlineSuggestionsRendererInfo(in RemoteCallback callback);
+
+ /**
+ * Releases the inline suggestion SurfaceControlViewHosts hosted in the service, for the
+ * provided userId and sessionId.
+ */
+ void destroySuggestionViews(int userId, int sessionId);
}
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index 9cf1b87f7eabd9408d1164eeebd32a797c335f7e..914169485979831e18c77339dad8f8c2e9188940 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -50,7 +50,11 @@ public final class InlinePresentation implements Parcelable {
/**
* Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
- * host.
+ * host. However, it's eventually up to the host whether the UI is pinned or not.
+ *
+ *
Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+ * new data set returned from authentication intent. See
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
*/
private final boolean mPinned;
@@ -90,7 +94,11 @@ public final class InlinePresentation implements Parcelable {
* Specifies the UI specification for the inline suggestion.
* @param pinned
* Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
- * host.
+ * host. However, it's eventually up to the host whether the UI is pinned or not.
+ *
+ *
Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+ * new data set returned from authentication intent. See
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
*/
@DataClass.Generated.Member
public InlinePresentation(
@@ -126,7 +134,11 @@ public final class InlinePresentation implements Parcelable {
/**
* Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
- * host.
+ * host. However, it's eventually up to the host whether the UI is pinned or not.
+ *
+ *
Also a {@link Dataset} with a pinned inline presentation will not be replaced by the
+ * new data set returned from authentication intent. See
+ * {@link Dataset.Builder#setAuthentication(android.content.IntentSender)} for more information.
*/
@DataClass.Generated.Member
public boolean isPinned() {
@@ -232,7 +244,7 @@ public final class InlinePresentation implements Parcelable {
};
@DataClass.Generated(
- time = 1586992400667L,
+ time = 1593131904745L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 3ea443bab3f803427ecc654b1b7eaa9f32dddca2..839caff5c3d4dd973d23f2b10a637f07cf519b67 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -41,6 +41,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
/**
@@ -64,7 +66,7 @@ public abstract class InlineSuggestionRenderService extends Service {
public static final String SERVICE_INTERFACE =
"android.service.autofill.InlineSuggestionRenderService";
- private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper(), null, true);
private IInlineSuggestionUiCallback mCallback;
@@ -82,7 +84,7 @@ public abstract class InlineSuggestionRenderService extends Service {
Boolean newValue) {
if (evicted) {
Log.w(TAG,
- "Hit max=100 entries in the cache. Releasing oldest one to make "
+ "Hit max=30 entries in the cache. Releasing oldest one to make "
+ "space.");
key.releaseSurfaceControlViewHost();
}
@@ -130,7 +132,7 @@ public abstract class InlineSuggestionRenderService extends Service {
private void handleRenderSuggestion(IInlineSuggestionUiCallback callback,
InlinePresentation presentation, int width, int height, IBinder hostInputToken,
- int displayId) {
+ int displayId, int userId, int sessionId) {
if (hostInputToken == null) {
try {
callback.onError();
@@ -192,15 +194,23 @@ public abstract class InlineSuggestionRenderService extends Service {
}
return true;
});
-
- try {
- InlineSuggestionUiImpl uiImpl = new InlineSuggestionUiImpl(host, mHandler);
- mActiveInlineSuggestions.put(uiImpl, true);
- callback.onContent(new InlineSuggestionUiWrapper(uiImpl), host.getSurfacePackage(),
- measuredSize.getWidth(), measuredSize.getHeight());
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException calling onContent()");
- }
+ final InlineSuggestionUiImpl uiImpl = new InlineSuggestionUiImpl(host, mMainHandler,
+ userId, sessionId);
+ mActiveInlineSuggestions.put(uiImpl, true);
+
+ // We post the callback invocation to the end of the main thread handler queue, to make
+ // sure the callback happens after the views are drawn. This is needed because calling
+ // {@link SurfaceControlViewHost#setView()} will post a task to the main thread
+ // to draw the view asynchronously.
+ mMainHandler.post(() -> {
+ try {
+ callback.onContent(new InlineSuggestionUiWrapper(uiImpl),
+ host.getSurfacePackage(),
+ measuredSize.getWidth(), measuredSize.getHeight());
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onContent()");
+ }
+ });
} finally {
updateDisplay(Display.DEFAULT_DISPLAY);
}
@@ -211,6 +221,18 @@ public abstract class InlineSuggestionRenderService extends Service {
callback.sendResult(rendererInfo);
}
+ private void handleDestroySuggestionViews(int userId, int sessionId) {
+ Log.v(TAG, "handleDestroySuggestionViews called for " + userId + ":" + sessionId);
+ for (final InlineSuggestionUiImpl inlineSuggestionUi :
+ mActiveInlineSuggestions.snapshot().keySet()) {
+ if (inlineSuggestionUi.mUserId == userId
+ && inlineSuggestionUi.mSessionId == sessionId) {
+ Log.v(TAG, "Destroy " + inlineSuggestionUi);
+ inlineSuggestionUi.releaseSurfaceControlViewHost();
+ }
+ }
+ }
+
/**
* A wrapper class around the {@link InlineSuggestionUiImpl} to ensure it's not strongly
* reference by the remote system server process.
@@ -253,10 +275,15 @@ public abstract class InlineSuggestionRenderService extends Service {
private SurfaceControlViewHost mViewHost;
@NonNull
private final Handler mHandler;
+ private final int mUserId;
+ private final int mSessionId;
- InlineSuggestionUiImpl(SurfaceControlViewHost viewHost, Handler handler) {
+ InlineSuggestionUiImpl(SurfaceControlViewHost viewHost, Handler handler, int userId,
+ int sessionId) {
this.mViewHost = viewHost;
this.mHandler = handler;
+ this.mUserId = userId;
+ this.mSessionId = sessionId;
}
/**
@@ -295,6 +322,16 @@ public abstract class InlineSuggestionRenderService extends Service {
}
}
+ /** @hide */
+ @Override
+ protected final void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
+ @NonNull String[] args) {
+ pw.println("mActiveInlineSuggestions: " + mActiveInlineSuggestions.size());
+ for (InlineSuggestionUiImpl impl : mActiveInlineSuggestions.snapshot().keySet()) {
+ pw.printf("ui: [%s] - [%d] [%d]\n", impl, impl.mUserId, impl.mSessionId);
+ }
+ }
+
@Override
@Nullable
public final IBinder onBind(@NonNull Intent intent) {
@@ -304,19 +341,26 @@ public abstract class InlineSuggestionRenderService extends Service {
@Override
public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
@NonNull InlinePresentation presentation, int width, int height,
- @Nullable IBinder hostInputToken, int displayId) {
- mHandler.sendMessage(
+ @Nullable IBinder hostInputToken, int displayId, int userId,
+ int sessionId) {
+ mMainHandler.sendMessage(
obtainMessage(InlineSuggestionRenderService::handleRenderSuggestion,
InlineSuggestionRenderService.this, callback, presentation,
- width, height, hostInputToken, displayId));
+ width, height, hostInputToken, displayId, userId, sessionId));
}
@Override
public void getInlineSuggestionsRendererInfo(@NonNull RemoteCallback callback) {
- mHandler.sendMessage(obtainMessage(
+ mMainHandler.sendMessage(obtainMessage(
InlineSuggestionRenderService::handleGetInlineSuggestionsRendererInfo,
InlineSuggestionRenderService.this, callback));
}
+ @Override
+ public void destroySuggestionViews(int userId, int sessionId) {
+ mMainHandler.sendMessage(obtainMessage(
+ InlineSuggestionRenderService::handleDestroySuggestionViews,
+ InlineSuggestionRenderService.this, userId, sessionId));
+ }
}.asBinder();
}
diff --git a/core/java/android/service/autofill/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java
index c879653859d8f48612037833d91e88655e29c14d..16c3f1d4e476e74b8a4bed4f90add3dadeabe192 100644
--- a/core/java/android/service/autofill/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -58,7 +58,9 @@ public class InlineSuggestionRoot extends FrameLayout {
case MotionEvent.ACTION_DOWN: {
mDownX = event.getX();
mDownY = event.getY();
- } break;
+ }
+ // Intentionally fall through to the next case so that when the window is obscured
+ // we transfer the touch to the remote IME window and don't handle it locally.
case MotionEvent.ACTION_MOVE: {
final float distance = MathUtils.dist(mDownX, mDownY,
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index c2234bad3803b73d4458fb641f75623511ec83e7..95cc64ae8aab5eeff76190513df07e27f715c86c 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -564,9 +564,9 @@ public abstract class AugmentedAutofillService extends Service {
}
void reportResult(@Nullable List inlineSuggestionsData,
- @Nullable Bundle clientState) {
+ @Nullable Bundle clientState, boolean showingFillWindow) {
try {
- mCallback.onSuccess(inlineSuggestionsData, clientState);
+ mCallback.onSuccess(inlineSuggestionsData, clientState, showingFillWindow);
} catch (RemoteException e) {
Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
}
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 21738d80f2d322de0ce289db77c5d269ae7ba28f..fc3baf1c9836e4fc3c5489890c7446139a3366d5 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -56,22 +56,24 @@ public final class FillCallback {
if (response == null) {
mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
- mProxy.reportResult(/* inlineSuggestionsData */ null, /* clientState */ null);
+ mProxy.reportResult(/* inlineSuggestionsData */ null, /* clientState */
+ null, /* showingFillWindow */ false);
return;
}
- List inlineSuggestions = response.getInlineSuggestions();
- Bundle clientState = response.getClientState();
+ final List inlineSuggestions = response.getInlineSuggestions();
+ final Bundle clientState = response.getClientState();
+ final FillWindow fillWindow = response.getFillWindow();
+ boolean showingFillWindow = false;
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
- mProxy.reportResult(inlineSuggestions, clientState);
- return;
- }
-
- final FillWindow fillWindow = response.getFillWindow();
- if (fillWindow != null) {
+ } else if (fillWindow != null) {
fillWindow.show();
+ showingFillWindow = true;
}
+ // We need to report result regardless of whether inline suggestions are returned or not.
+ mProxy.reportResult(inlineSuggestions, clientState, showingFillWindow);
+
// TODO(b/123099468): must notify the server so it can update the session state to avoid
// showing conflicting UIs (for example, if a new request is made to the main autofill
// service and it now wants to show something).
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 077df6cf16ef1796ed5a6f70b86e65fdc884563f..8e866466e8dfd54d73f3a4203a6e0d1df70287dc 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -208,12 +208,18 @@ public final class FillWindow implements AutoCloseable {
if (sDebug) Log.d(TAG, "handleShow()");
synchronized (mLock) {
if (mWm != null && mFillView != null) {
- p.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
- if (!mShowing) {
- mWm.addView(mFillView, p);
- mShowing = true;
- } else {
- mWm.updateViewLayout(mFillView, p);
+ try {
+ p.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+ if (!mShowing) {
+ mWm.addView(mFillView, p);
+ mShowing = true;
+ } else {
+ mWm.updateViewLayout(mFillView, p);
+ }
+ } catch (WindowManager.BadTokenException e) {
+ if (sDebug) Log.d(TAG, "Filed with token " + p.token + " gone.");
+ } catch (IllegalStateException e) {
+ if (sDebug) Log.d(TAG, "Exception showing window.");
}
}
}
@@ -223,8 +229,12 @@ public final class FillWindow implements AutoCloseable {
if (sDebug) Log.d(TAG, "handleHide()");
synchronized (mLock) {
if (mWm != null && mFillView != null && mShowing) {
- mWm.removeView(mFillView);
- mShowing = false;
+ try {
+ mWm.removeView(mFillView);
+ mShowing = false;
+ } catch (IllegalStateException e) {
+ if (sDebug) Log.d(TAG, "Exception hiding window.");
+ }
}
}
}
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index 609e382e2b9622499fd6d551b37e78877dcf4413..4dfdd4db27e53ced1b074646a7254cfd04ecb882 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -30,7 +30,9 @@ import java.util.List;
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
- void onSuccess(in @nullable List inlineSuggestionsData, in @nullable Bundle clientState);
+ void onSuccess(in @nullable List inlineSuggestionsData,
+ in @nullable Bundle clientState,
+ boolean showingFillWindow);
boolean isCompleted();
void cancel();
}
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
index d01bc2524332bb71f4af7de08ce58f3001a08ee2..2868f1bf354713dc72eb83415a08649169e1c293 100644
--- a/core/java/android/service/controls/Control.java
+++ b/core/java/android/service/controls/Control.java
@@ -73,25 +73,37 @@ public final class Control implements Parcelable {
})
public @interface Status {};
+ /**
+ * Reserved for use with the {@link StatelessBuilder}, and while loading. When state is
+ * requested via {@link ControlsProviderService#createPublisherFor}, use other status codes
+ * to indicate the proper device state.
+ */
public static final int STATUS_UNKNOWN = 0;
/**
- * The device corresponding to the {@link Control} is responding correctly.
+ * Used to indicate that the state of the device was successfully retrieved. This includes
+ * all scenarios where the device may have a warning for the user, such as "Lock jammed",
+ * or "Vacuum stuck". Any information for the user should be set through
+ * {@link StatefulBuilder#setStatusText}.
*/
public static final int STATUS_OK = 1;
/**
- * The device corresponding to the {@link Control} cannot be found or was removed.
+ * The device corresponding to the {@link Control} cannot be found or was removed. The user
+ * will be alerted and directed to the application to resolve.
*/
public static final int STATUS_NOT_FOUND = 2;
/**
- * The device corresponding to the {@link Control} is in an error state.
+ * Used to indicate that there was a temporary error while loading the device state. A default
+ * error message will be displayed in place of any custom text that was set through
+ * {@link StatefulBuilder#setStatusText}.
*/
public static final int STATUS_ERROR = 3;
/**
- * The {@link Control} is currently disabled.
+ * The {@link Control} is currently disabled. A default error message will be displayed in
+ * place of any custom text that was set through {@link StatefulBuilder#setStatusText}.
*/
public static final int STATUS_DISABLED = 4;
@@ -777,6 +789,13 @@ public final class Control implements Parcelable {
}
/**
+ * Set the {@link ControlTemplate} to define the primary user interaction
+ *
+ * Devices may support a variety of user interactions, and all interactions cannot be
+ * represented with a single {@link ControlTemplate}. Therefore, the selected template
+ * should be most closely aligned with what the expected primary device action will be.
+ * Any secondary interactions can be done via the {@link #setAppIntent(PendingIntent)}.
+ *
* @param controlTemplate instance of {@link ControlTemplate}, that defines how the
* {@link Control} will behave and what interactions are
* available to the user
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index 4e5aa0018b618cd7e7477cdf6b1ea208bc7a8487..6bd376a19fc5ea6a132ef4d20d4cf4e59eb1aacf 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -296,6 +296,10 @@ public abstract class ControlsProviderService extends Service {
/**
* Request SystemUI to prompt the user to add a control to favorites.
+ *
+ * SystemUI may not honor this request in some cases, for example if the requested
+ * {@link Control} is already a favorite, or the requesting package is not currently in the
+ * foreground.
*
* @param context A context
* @param componentName Component name of the {@link ControlsProviderService}
diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java
index 1e16273c455b64e5796c83792e34db09681b3680..e592fad394b84af87c6cac9bcf3818c0a428866a 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -214,10 +214,13 @@ public abstract class ControlTemplate {
}
/**
- * Get a singleton {@link ControlTemplate} that has no features.
+ * Get a singleton {@link ControlTemplate}, which supports no direct user input.
*
- * This template has no distinctive field, not even an identifier. Used for a {@link Control}
- * that accepts no type of input, or when there is no known state.
+ * Used by {@link Control.StatelessBuilder} when there is no known state. Can also be used
+ * in {@link Control.StatefulBuilder} for conveying information to a user about the
+ * {@link Control} but direct user interaction is not desired. Since this template has no
+ * corresponding {@link ControlAction}, any user interaction will launch the
+ * {@link Control#getAppIntent()}.
*
* @return a singleton {@link ControlTemplate} to indicate no specific template is used by
* this {@link Control}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1f6555c85a667300c46c5027e6aab0a4d293c3c0..0827fef602522042992a415b0885910b5a8a82d4 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -78,6 +78,7 @@ public class ZenModeConfig implements Parcelable {
private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
+ public static final String MANUAL_RULE_ID = "MANUAL_RULE";
public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
public static final List DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID,
@@ -958,6 +959,48 @@ public class ZenModeConfig implements Parcelable {
}
};
+ /**
+ * Converts a ZenModeConfig to a ZenPolicy
+ */
+ public ZenPolicy toZenPolicy() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder()
+ .allowCalls(allowCalls
+ ? ZenModeConfig.getZenPolicySenders(allowCallsFrom)
+ : ZenPolicy.PEOPLE_TYPE_NONE)
+ .allowRepeatCallers(allowRepeatCallers)
+ .allowMessages(allowMessages
+ ? ZenModeConfig.getZenPolicySenders(allowMessagesFrom)
+ : ZenPolicy.PEOPLE_TYPE_NONE)
+ .allowReminders(allowReminders)
+ .allowEvents(allowEvents)
+ .allowAlarms(allowAlarms)
+ .allowMedia(allowMedia)
+ .allowSystem(allowSystem)
+ .allowConversations(allowConversations
+ ? ZenModeConfig.getZenPolicySenders(allowConversationsFrom)
+ : ZenPolicy.PEOPLE_TYPE_NONE);
+ if (suppressedVisualEffects == 0) {
+ builder.showAllVisualEffects();
+ } else {
+ // configs don't have an unset state: wither true or false.
+ builder.showFullScreenIntent(
+ (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0);
+ builder.showLights(
+ (suppressedVisualEffects & SUPPRESSED_EFFECT_LIGHTS) == 0);
+ builder.showPeeking(
+ (suppressedVisualEffects & SUPPRESSED_EFFECT_PEEK) == 0);
+ builder.showStatusBarIcons(
+ (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_STATUS_BAR) == 0);
+ builder.showBadges(
+ (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_BADGE) == 0);
+ builder.showInAmbientDisplay(
+ (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_AMBIENT) == 0);
+ builder.showInNotificationList(
+ (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0);
+ }
+ return builder.build();
+ }
+
/**
* Converts a zenPolicy to a notificationPolicy using this ZenModeConfig's values as its
* defaults for all unset values in zenPolicy
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 87295e1c95b98d5280083a21659550e6a484de4c..6d0bcffe148e55d923d6aac1d8a1ab8498065c5d 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -24,6 +24,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.proto.ProtoOutputStream;
+import java.io.ByteArrayOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -1110,6 +1111,40 @@ public final class ZenPolicy implements Parcelable {
proto.end(token);
}
+ /**
+ * Converts a policy to a statsd proto.
+ * @hides
+ */
+ public byte[] toProto() {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ ProtoOutputStream proto = new ProtoOutputStream(bytes);
+
+ proto.write(DNDPolicyProto.CALLS, getPriorityCategoryCalls());
+ proto.write(DNDPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers());
+ proto.write(DNDPolicyProto.MESSAGES, getPriorityCategoryMessages());
+ proto.write(DNDPolicyProto.CONVERSATIONS, getPriorityCategoryConversations());
+ proto.write(DNDPolicyProto.REMINDERS, getPriorityCategoryReminders());
+ proto.write(DNDPolicyProto.EVENTS, getPriorityCategoryEvents());
+ proto.write(DNDPolicyProto.ALARMS, getPriorityCategoryAlarms());
+ proto.write(DNDPolicyProto.MEDIA, getPriorityCategoryMedia());
+ proto.write(DNDPolicyProto.SYSTEM, getPriorityCategorySystem());
+
+ proto.write(DNDPolicyProto.FULLSCREEN, getVisualEffectFullScreenIntent());
+ proto.write(DNDPolicyProto.LIGHTS, getVisualEffectLights());
+ proto.write(DNDPolicyProto.PEEK, getVisualEffectPeek());
+ proto.write(DNDPolicyProto.STATUS_BAR, getVisualEffectStatusBar());
+ proto.write(DNDPolicyProto.BADGE, getVisualEffectBadge());
+ proto.write(DNDPolicyProto.AMBIENT, getVisualEffectAmbient());
+ proto.write(DNDPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList());
+
+ proto.write(DNDPolicyProto.ALLOW_CALLS_FROM, getPriorityCallSenders());
+ proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM, getPriorityMessageSenders());
+ proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM, getPriorityConversationSenders());
+
+ proto.flush();
+ return bytes.toByteArray();
+ }
+
/**
* Makes deep copy of this ZenPolicy.
* @hide
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 4a0dd870f797aabca3a35b078aabc4606b202c5a..0341b6d96b2947d010418bc541b519d1d69ec2e1 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1311,7 +1311,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
throw new IllegalStateException("Can't call before onCreate()");
}
try {
- intent.migrateExtraStreamToClipData();
+ intent.migrateExtraStreamToClipData(mContext);
intent.prepareToLeaveProcess(mContext);
int res = mSystemService.startVoiceActivity(mToken, intent,
intent.resolveType(mContext.getContentResolver()),
@@ -1340,7 +1340,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
throw new IllegalStateException("Can't call before onCreate()");
}
try {
- intent.migrateExtraStreamToClipData();
+ intent.migrateExtraStreamToClipData(mContext);
intent.prepareToLeaveProcess(mContext);
int res = mSystemService.startAssistantActivity(mToken, intent,
intent.resolveType(mContext.getContentResolver()),
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f944dd78dc3d62de6482b075305382a27deadd7d..0d420c5936ae8c7acb5c2ef72a9c62dbc635cfdc 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1184,7 +1184,8 @@ public abstract class WallpaperService extends Service {
// may have been destroyed so now we need to make
// sure it is re-created.
doOffsetsChanged(false);
- updateSurface(false, false, false);
+ // force relayout to get new surface
+ updateSurface(true, false, false);
}
onVisibilityChanged(visible);
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index e4fbf9f0e187bc0348f4a19033670538c54371c8..4adcd6948f85cacb5f8877391eeb31b5207f262f 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -157,8 +157,8 @@ public class PhoneStateListener {
* Listen for changes to the device's cell location. Note that
* this will result in frequent callbacks to the listener.
* {@more}
- * Requires Permission: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION
- * ACCESS_COARSE_LOCATION}
+ * Requires Permission: {@link android.Manifest.permission#ACCESS_FINE_LOCATION
+ * ACCESS_FINE_LOCATION}
*
* If you need regular location updates but want more control over
* the update interval or location precision, you can set up a listener
@@ -219,6 +219,9 @@ public class PhoneStateListener {
/**
* Listen for changes to observed cell info.
*
+ * Listening to this event requires the {@link Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission.
+ *
* @see #onCellInfoChanged
*/
public static final int LISTEN_CELL_INFO = 0x00000400;
@@ -340,6 +343,10 @@ public class PhoneStateListener {
/**
* 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}).
+ *
* @see #onDisplayInfoChanged
*/
public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000;
@@ -457,6 +464,9 @@ public class PhoneStateListener {
*
Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
* the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
*
+ *
Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+ * of whether the calling app has carrier privileges.
+ *
* @see #onRegistrationFailed
*/
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -468,6 +478,9 @@ public class PhoneStateListener {
*
Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
* the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
*
+ *
Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+ * of whether the calling app has carrier privileges.
+ *
* @see #onBarringInfoChanged
*/
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -565,6 +578,11 @@ public class PhoneStateListener {
* subId. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
+ * The instance of {@link ServiceState} passed as an argument here will have various levels of
+ * location information stripped from it depending on the location permissions that your app
+ * holds. Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
+ * receive all the information in {@link ServiceState}.
+ *
* @see ServiceState#STATE_EMERGENCY_ONLY
* @see ServiceState#STATE_IN_SERVICE
* @see ServiceState#STATE_OUT_OF_SERVICE
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 8acf5fa8bdfe65b8696a26a30bf0ddbe2c8e4b91..537498c44d5e077c3a9ca50a169689cb434643bb 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -67,7 +67,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
- DEFAULT_FLAGS.put("settings_contextual_home2", "true");
}
/**
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 2a4b65d23e64764e98040aa38ccc2a5750677332..6efe95cb9e92d3b5f3563765bef76d8fbb57ccb3 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -420,6 +420,7 @@ final class ApkSigningBlockUtils {
static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
+ static final int CONTENT_DIGEST_SHA256 = 4;
private static final int[] V4_CONTENT_DIGEST_ALGORITHMS =
{CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
diff --git a/core/java/android/util/apk/SourceStampVerifier.java b/core/java/android/util/apk/SourceStampVerifier.java
index a7ae32d1baa2d8ecfa8193056526146c43a9c113..5fc242353d5192845252c9146fedadf9c94ccd17 100644
--- a/core/java/android/util/apk/SourceStampVerifier.java
+++ b/core/java/android/util/apk/SourceStampVerifier.java
@@ -16,6 +16,7 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_SHA256;
import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContentDigestAlgorithm;
@@ -27,12 +28,10 @@ import android.util.Pair;
import android.util.Slog;
import android.util.jar.StrictJarFile;
-import libcore.io.IoUtils;
+import libcore.io.Streams;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
@@ -49,11 +48,13 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
+import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
/**
@@ -74,7 +75,11 @@ public abstract class SourceStampVerifier {
private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0;
- private static final int SOURCE_STAMP_BLOCK_ID = 0x2b09189e;
+ private static final int SOURCE_STAMP_BLOCK_ID = 0x6dff800d;
+
+ private static final int VERSION_JAR_SIGNATURE_SCHEME = 1;
+ private static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2;
+ private static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3;
/** Name of the SourceStamp certificate hash ZIP entry in APKs. */
private static final String SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME = "stamp-cert-sha256";
@@ -115,7 +120,8 @@ public abstract class SourceStampVerifier {
// SourceStamp present.
return SourceStampVerificationResult.notPresent();
}
- return verify(apk, sourceStampCertificateDigest);
+ byte[] manifestBytes = getManifestBytes(apkJar);
+ return verify(apk, sourceStampCertificateDigest, manifestBytes);
} catch (IOException e) {
// Any exception in reading the APK returns a non-present SourceStamp outcome
// without affecting the outcome of any of the other signature schemes.
@@ -126,22 +132,71 @@ public abstract class SourceStampVerifier {
}
private static SourceStampVerificationResult verify(
- RandomAccessFile apk, byte[] sourceStampCertificateDigest) {
+ RandomAccessFile apk, byte[] sourceStampCertificateDigest, byte[] manifestBytes) {
try {
SignatureInfo signatureInfo =
ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID);
- Map apkContentDigests = getApkContentDigests(apk);
- return verify(signatureInfo, apkContentDigests, sourceStampCertificateDigest);
- } catch (IOException | SignatureNotFoundException e) {
+ Map> signatureSchemeApkContentDigests =
+ getSignatureSchemeApkContentDigests(apk, manifestBytes);
+ return verify(
+ signatureInfo,
+ getSignatureSchemeDigests(signatureSchemeApkContentDigests),
+ sourceStampCertificateDigest);
+ } catch (IOException | SignatureNotFoundException | RuntimeException e) {
return SourceStampVerificationResult.notVerified();
}
}
private static SourceStampVerificationResult verify(
SignatureInfo signatureInfo,
- Map apkContentDigests,
+ Map signatureSchemeDigests,
byte[] sourceStampCertificateDigest)
throws SecurityException, IOException {
+ ByteBuffer sourceStampBlock = signatureInfo.signatureBlock;
+ ByteBuffer sourceStampBlockData =
+ ApkSigningBlockUtils.getLengthPrefixedSlice(sourceStampBlock);
+
+ X509Certificate sourceStampCertificate =
+ verifySourceStampCertificate(sourceStampBlockData, sourceStampCertificateDigest);
+
+ // Parse signed signature schemes block.
+ ByteBuffer signedSignatureSchemes =
+ ApkSigningBlockUtils.getLengthPrefixedSlice(sourceStampBlockData);
+ Map signedSignatureSchemeData = new HashMap<>();
+ while (signedSignatureSchemes.hasRemaining()) {
+ ByteBuffer signedSignatureScheme =
+ ApkSigningBlockUtils.getLengthPrefixedSlice(signedSignatureSchemes);
+ int signatureSchemeId = signedSignatureScheme.getInt();
+ signedSignatureSchemeData.put(signatureSchemeId, signedSignatureScheme);
+ }
+
+ for (Map.Entry signatureSchemeDigest : signatureSchemeDigests.entrySet()) {
+ if (!signedSignatureSchemeData.containsKey(signatureSchemeDigest.getKey())) {
+ throw new SecurityException(
+ String.format(
+ "No signatures found for signature scheme %d",
+ signatureSchemeDigest.getKey()));
+ }
+ verifySourceStampSignature(
+ signedSignatureSchemeData.get(signatureSchemeDigest.getKey()),
+ sourceStampCertificate,
+ signatureSchemeDigest.getValue());
+ }
+
+ return SourceStampVerificationResult.verified(sourceStampCertificate);
+ }
+
+ /**
+ * Verify the SourceStamp certificate found in the signing block is the same as the SourceStamp
+ * certificate found in the APK. It returns the verified certificate.
+ *
+ * @param sourceStampBlockData the source stamp block in the APK signing block which contains
+ * the certificate used to sign the stamp digests.
+ * @param sourceStampCertificateDigest the source stamp certificate digest found in the APK.
+ */
+ private static X509Certificate verifySourceStampCertificate(
+ ByteBuffer sourceStampBlockData, byte[] sourceStampCertificateDigest)
+ throws IOException {
CertificateFactory certFactory;
try {
certFactory = CertificateFactory.getInstance("X.509");
@@ -149,17 +204,6 @@ public abstract class SourceStampVerifier {
throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
}
- List> digests =
- apkContentDigests.entrySet().stream()
- .sorted(Map.Entry.comparingByKey())
- .map(e -> Pair.create(e.getKey(), e.getValue()))
- .collect(Collectors.toList());
- byte[] digestBytes = encodeApkContentDigests(digests);
-
- ByteBuffer sourceStampBlock = signatureInfo.signatureBlock;
- ByteBuffer sourceStampBlockData =
- ApkSigningBlockUtils.getLengthPrefixedSlice(sourceStampBlock);
-
// Parse the SourceStamp certificate.
byte[] sourceStampEncodedCertificate =
ApkSigningBlockUtils.readLengthPrefixedByteArray(sourceStampBlockData);
@@ -172,24 +216,30 @@ public abstract class SourceStampVerifier {
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate", e);
}
- sourceStampCertificate =
- new VerbatimX509Certificate(sourceStampCertificate, sourceStampEncodedCertificate);
- // Verify the SourceStamp certificate found in the signing block is the same as the
- // SourceStamp certificate found in the APK.
- try {
- MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
- messageDigest.update(sourceStampEncodedCertificate);
- byte[] sourceStampBlockCertificateDigest = messageDigest.digest();
- if (!Arrays.equals(sourceStampCertificateDigest, sourceStampBlockCertificateDigest)) {
- throw new SecurityException("Certificate mismatch between APK and signature block");
- }
- } catch (NoSuchAlgorithmException e) {
- throw new SecurityException("Failed to find SHA-256", e);
+ byte[] sourceStampBlockCertificateDigest =
+ computeSha256Digest(sourceStampEncodedCertificate);
+ if (!Arrays.equals(sourceStampCertificateDigest, sourceStampBlockCertificateDigest)) {
+ throw new SecurityException("Certificate mismatch between APK and signature block");
}
+ return new VerbatimX509Certificate(sourceStampCertificate, sourceStampEncodedCertificate);
+ }
+
+ /**
+ * Verify the SourceStamp signature found in the signing block is signed by the SourceStamp
+ * certificate found in the APK.
+ *
+ * @param signedBlockData the source stamp block in the APK signing block which contains the
+ * stamp signed digests.
+ * @param sourceStampCertificate the source stamp certificate used to sign the stamp digests.
+ * @param digest the digest to be verified being signed by the source stamp certificate.
+ */
+ private static void verifySourceStampSignature(
+ ByteBuffer signedBlockData, X509Certificate sourceStampCertificate, byte[] digest)
+ throws IOException {
// Parse the signatures block and identify supported signatures
- ByteBuffer signatures = ApkSigningBlockUtils.getLengthPrefixedSlice(sourceStampBlockData);
+ ByteBuffer signatures = ApkSigningBlockUtils.getLengthPrefixedSlice(signedBlockData);
int signatureCount = 0;
int bestSigAlgorithm = -1;
byte[] bestSigAlgorithmSignatureBytes = null;
@@ -235,7 +285,7 @@ public abstract class SourceStampVerifier {
if (jcaSignatureAlgorithmParams != null) {
sig.setParameter(jcaSignatureAlgorithmParams);
}
- sig.update(digestBytes);
+ sig.update(digest);
sigVerified = sig.verify(bestSigAlgorithmSignatureBytes);
} catch (InvalidKeyException
| InvalidAlgorithmParameterException
@@ -247,27 +297,44 @@ public abstract class SourceStampVerifier {
if (!sigVerified) {
throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify");
}
-
- return SourceStampVerificationResult.verified(sourceStampCertificate);
}
- private static Map getApkContentDigests(RandomAccessFile apk)
- throws IOException, SignatureNotFoundException {
- // Retrieve APK content digests in V3 signing block. If a V3 signature is not found, the APK
- // content digests would be re-tried from V2 signature.
+ private static Map> getSignatureSchemeApkContentDigests(
+ RandomAccessFile apk, byte[] manifestBytes) throws IOException {
+ Map> signatureSchemeApkContentDigests = new HashMap<>();
+
+ // Retrieve APK content digests in V3 signing block.
try {
SignatureInfo v3SignatureInfo =
ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID);
- return getApkContentDigestsFromSignatureBlock(v3SignatureInfo.signatureBlock);
+ signatureSchemeApkContentDigests.put(
+ VERSION_APK_SIGNATURE_SCHEME_V3,
+ getApkContentDigestsFromSignatureBlock(v3SignatureInfo.signatureBlock));
} catch (SignatureNotFoundException e) {
// It's fine not to find a V3 signature.
}
- // Retrieve APK content digests in V2 signing block. If a V2 signature is not found, the
- // process of retrieving APK content digests stops, and the stamp is considered un-verified.
- SignatureInfo v2SignatureInfo =
- ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID);
- return getApkContentDigestsFromSignatureBlock(v2SignatureInfo.signatureBlock);
+ // Retrieve APK content digests in V2 signing block.
+ try {
+ SignatureInfo v2SignatureInfo =
+ ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID);
+ signatureSchemeApkContentDigests.put(
+ VERSION_APK_SIGNATURE_SCHEME_V2,
+ getApkContentDigestsFromSignatureBlock(v2SignatureInfo.signatureBlock));
+ } catch (SignatureNotFoundException e) {
+ // It's fine not to find a V2 signature.
+ }
+
+ // Retrieve manifest digest.
+ if (manifestBytes != null) {
+ Map jarSignatureSchemeApkContentDigests = new HashMap<>();
+ jarSignatureSchemeApkContentDigests.put(
+ CONTENT_DIGEST_SHA256, computeSha256Digest(manifestBytes));
+ signatureSchemeApkContentDigests.put(
+ VERSION_JAR_SIGNATURE_SCHEME, jarSignatureSchemeApkContentDigests);
+ }
+
+ return signatureSchemeApkContentDigests;
}
private static Map getApkContentDigestsFromSignatureBlock(
@@ -289,27 +356,45 @@ public abstract class SourceStampVerifier {
return apkContentDigests;
}
- private static byte[] getSourceStampCertificateDigest(StrictJarFile apkJar) throws IOException {
- InputStream inputStream = null;
- try {
- ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME);
- if (zipEntry == null) {
- // SourceStamp certificate hash file not found, which means that there is not
- // SourceStamp present.
- return null;
- }
- inputStream = apkJar.getInputStream(zipEntry);
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ private static Map getSignatureSchemeDigests(
+ Map> signatureSchemeApkContentDigests) {
+ Map digests = new HashMap<>();
+ for (Map.Entry> signatureSchemeApkContentDigest :
+ signatureSchemeApkContentDigests.entrySet()) {
+ List> apkDigests =
+ getApkDigests(signatureSchemeApkContentDigest.getValue());
+ digests.put(
+ signatureSchemeApkContentDigest.getKey(), encodeApkContentDigests(apkDigests));
+ }
+ return digests;
+ }
- // Trying to read the certificate digest, which should be less than 1024 bytes.
- byte[] buffer = new byte[1024];
- int count = inputStream.read(buffer, 0, buffer.length);
- byteArrayOutputStream.write(buffer, 0, count);
+ private static List> getApkDigests(
+ Map apkContentDigests) {
+ List> digests = new ArrayList<>();
+ for (Map.Entry apkContentDigest : apkContentDigests.entrySet()) {
+ digests.add(Pair.create(apkContentDigest.getKey(), apkContentDigest.getValue()));
+ }
+ digests.sort(Comparator.comparing(pair -> pair.first));
+ return digests;
+ }
- return byteArrayOutputStream.toByteArray();
- } finally {
- IoUtils.closeQuietly(inputStream);
+ private static byte[] getSourceStampCertificateDigest(StrictJarFile apkJar) throws IOException {
+ ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME);
+ if (zipEntry == null) {
+ // SourceStamp certificate hash file not found, which means that there is not
+ // SourceStamp present.
+ return null;
+ }
+ return Streams.readFully(apkJar.getInputStream(zipEntry));
+ }
+
+ private static byte[] getManifestBytes(StrictJarFile apkJar) throws IOException {
+ ZipEntry zipEntry = apkJar.findEntry(JarFile.MANIFEST_NAME);
+ if (zipEntry == null) {
+ return null;
}
+ return Streams.readFully(apkJar.getInputStream(zipEntry));
}
private static byte[] encodeApkContentDigests(List> apkContentDigests) {
@@ -329,6 +414,16 @@ public abstract class SourceStampVerifier {
return result.array();
}
+ private static byte[] computeSha256Digest(byte[] input) {
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+ messageDigest.update(input);
+ return messageDigest.digest();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Failed to find SHA-256", e);
+ }
+ }
+
private static void closeApkJar(StrictJarFile apkJar) {
try {
if (apkJar == null) {
diff --git a/core/java/android/util/apk/TEST_MAPPING b/core/java/android/util/apk/TEST_MAPPING
new file mode 100644
index 0000000000000000000000000000000000000000..8544e82e04e030f253a0630b71274162d2410b4a
--- /dev/null
+++ b/core/java/android/util/apk/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.util.apk.SourceStampVerifierTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 8db1703a627fd9ab2a1244aa46730b1542941779..0cc469a2d5eb06c84e8e8706f3dce05e5eb8e48d 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -241,12 +241,25 @@ public final class Display {
* This flag identifies secondary displays that should show system decorations, such as status
* bar, navigation bar, home activity or IME.
*
+ *
Note that this flag doesn't work without {@link #FLAG_TRUSTED}
*
+ * @see #getFlags()
* @hide
*/
// TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors
public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 6;
+ /**
+ * Flag: The display is trusted to show system decorations and receive inputs without users'
+ * touch.
+ * @see #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ *
+ * @see #getFlags()
+ * @hide
+ */
+ @TestApi
+ public static final int FLAG_TRUSTED = 1 << 7;
+
/**
* Display flag: Indicates that the contents of the display should not be scaled
* to fit the physical screen dimensions. Used for development only to emulate
@@ -564,6 +577,7 @@ public final class Display {
* @see #FLAG_SUPPORTS_PROTECTED_BUFFERS
* @see #FLAG_SECURE
* @see #FLAG_PRIVATE
+ * @see #FLAG_ROUND
*/
public int getFlags() {
return mFlags;
@@ -1222,6 +1236,16 @@ public final class Display {
Display.FLAG_PRESENTATION;
}
+ /**
+ * @return {@code true} if the display is a trusted display.
+ *
+ * @see #FLAG_TRUSTED
+ * @hide
+ */
+ public boolean isTrusted() {
+ return (mFlags & FLAG_TRUSTED) == FLAG_TRUSTED;
+ }
+
private void updateDisplayInfoLocked() {
// Note: The display manager caches display info objects on our behalf.
DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId);
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index d369883f3ac3ffbc0de1b3e0b51d726dbfd9d492..b1ede4102bec0ff2c2e2c6ff5b2a03a51eba5b30 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -717,6 +717,15 @@ public final class DisplayInfo implements Parcelable {
if ((flags & Display.FLAG_ROUND) != 0) {
result.append(", FLAG_ROUND");
}
+ if ((flags & Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
+ result.append(", FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD");
+ }
+ if ((flags & Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+ result.append(", FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS");
+ }
+ if ((flags & Display.FLAG_TRUSTED) != 0) {
+ result.append(", FLAG_TRUSTED");
+ }
return result.toString();
}
}
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index f6c72c4eefbc9bf331413ee0306d2f42441ed770..55c527ba6fa6876a813bc32b939e7e67bc535b7a 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.os.StrictMode.vmIncorrectContextUseEnabled;
+
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
@@ -26,9 +28,12 @@ import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFI
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.StrictMode;
import android.os.SystemClock;
+import android.util.Log;
import com.android.internal.util.FrameworkStatsLog;
@@ -228,6 +233,7 @@ public class GestureDetector {
}
}
+ private static final String TAG = GestureDetector.class.getSimpleName();
@UnsupportedAppUsage
private int mTouchSlopSquare;
private int mDoubleTapTouchSlopSquare;
@@ -378,7 +384,8 @@ public class GestureDetector {
* You may only use this constructor from a {@link android.os.Looper} thread.
* @see android.os.Handler#Handler()
*
- * @param context the application's context
+ * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null. If the listener implements the {@link OnDoubleTapListener} or
* {@link OnContextClickListener} then it will also be set as the listener for
@@ -395,7 +402,8 @@ public class GestureDetector {
* thread associated with the supplied {@link android.os.Handler}.
* @see android.os.Handler#Handler()
*
- * @param context the application's context
+ * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null. If the listener implements the {@link OnDoubleTapListener} or
* {@link OnContextClickListener} then it will also be set as the listener for
@@ -425,7 +433,8 @@ public class GestureDetector {
* thread associated with the supplied {@link android.os.Handler}.
* @see android.os.Handler#Handler()
*
- * @param context the application's context
+ * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use for running deferred listener events.
@@ -456,6 +465,17 @@ public class GestureDetector {
mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
mAmbiguousGestureMultiplier = ViewConfiguration.getAmbiguousGestureMultiplier();
} else {
+ if (!context.isUiContext() && vmIncorrectContextUseEnabled()) {
+ final String errorMessage =
+ "Tried to access UI constants from a non-visual Context.";
+ final String message = "GestureDetector must be accessed from Activity or other "
+ + "visual Context. Use an Activity or a Context created with "
+ + "Context#createWindowContext(int, Bundle), which are adjusted to the "
+ + "configuration and visual bounds of an area on screen.";
+ final Exception exception = new IllegalArgumentException(errorMessage);
+ StrictMode.onIncorrectContextUsed(message, exception);
+ Log.e(TAG, errorMessage + message, exception);
+ }
final ViewConfiguration configuration = ViewConfiguration.get(context);
touchSlop = configuration.getScaledTouchSlop();
doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop();
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index a4800726bbe8bb50fa2029dbe2dca90097e590e7..ad43f9556d8d7fe852df3e8c27c3e0f26c6e3107 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -125,6 +125,13 @@ public final class ImeFocusController {
final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView;
onViewFocusChanged(viewForWindowFocus, true);
+ // Starting new input when the next focused view is same as served view but the
+ // editor is not aligned with the same editor or editor is inactive.
+ final boolean nextFocusIsServedView = mServedView != null && mServedView == focusedView;
+ if (nextFocusIsServedView && !immDelegate.isSameEditorAndAcceptingText(focusedView)) {
+ forceFocus = true;
+ }
+
immDelegate.startInputAsyncOnWindowFocusGain(viewForWindowFocus,
windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
}
@@ -247,6 +254,7 @@ public final class ImeFocusController {
void setCurrentRootView(ViewRootImpl rootView);
boolean isCurrentRootView(ViewRootImpl rootView);
boolean isRestartOnNextWindowFocus(boolean reset);
+ boolean isSameEditorAndAcceptingText(View view);
}
public View getServedView() {
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 8b5af29517cb5ecf2f400b7e5f24cb21c306e2a7..c1998c6009cf85c027e0310f12831ad87d97b793 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -21,6 +21,7 @@ import static android.view.InsetsState.ITYPE_IME;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
import android.os.Parcel;
import android.text.TextUtils;
import android.view.SurfaceControl.Transaction;
@@ -118,11 +119,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
// If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
// this code here means that we now got control, so we can start the animation immediately.
// If client window is trying to control IME and IME is already visible, it is immediate.
- if (fromIme || mState.getSource(getType()).isVisible()) {
+ if (fromIme || mState.getSource(getType()).isVisible() && getControl() != null) {
return ShowResult.SHOW_IMMEDIATELY;
}
- return getImm().requestImeShow(null /* resultReceiver */)
+ return getImm().requestImeShow(mController.getHost().getWindowToken())
? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED;
}
@@ -131,12 +132,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
*/
@Override
void notifyHidden() {
- getImm().notifyImeHidden();
+ getImm().notifyImeHidden(mController.getHost().getWindowToken());
}
@Override
public void removeSurface() {
- getImm().removeImeSurface();
+ final IBinder window = mController.getHost().getWindowToken();
+ if (window != null) {
+ getImm().removeImeSurface(window);
+ }
}
@Override
@@ -145,6 +149,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
super.setControl(control, showTypes, hideTypes);
if (control == null && !mIsRequestedVisibleAwaitingControl) {
hide();
+ removeSurface();
}
}
@@ -153,6 +158,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
return mIsRequestedVisibleAwaitingControl || isRequestedVisible();
}
+ @Override
+ public void onPerceptible(boolean perceptible) {
+ super.onPerceptible(perceptible);
+ final IBinder window = mController.getHost().getWindowToken();
+ if (window != null) {
+ getImm().reportPerceptible(window, perceptible);
+ }
+ }
+
private boolean isDummyOrEmptyEditor(EditorInfo info) {
// TODO(b/123044812): Handle dummy input gracefully in IME Insets API
return info == null || (info.fieldId <= 0 && info.inputType <= 0);
diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java
index 74c186948b2f2bd878ac7b8965cc516d14454393..3431c3ecc310f166ddcba675397e94ff4723fc85 100644
--- a/core/java/android/view/InsetsAnimationControlCallbacks.java
+++ b/core/java/android/view/InsetsAnimationControlCallbacks.java
@@ -16,6 +16,7 @@
package android.view;
+import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
/**
@@ -64,4 +65,15 @@ public interface InsetsAnimationControlCallbacks {
* previous calls to applySurfaceParams.
*/
void releaseSurfaceControlFromRt(SurfaceControl sc);
+
+ /**
+ * Reports that the perceptibility of the given types has changed to the given value.
+ *
+ * A type is perceptible if it is not (almost) entirely off-screen and not (almost) entirely
+ * transparent.
+ *
+ * @param types the (public) types whose perceptibility has changed
+ * @param perceptible true, if the types are now perceptible, false if they are not perceptible
+ */
+ void reportPerceptible(@InsetsType int types, boolean perceptible);
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index cd56ca9251abea2bfc90834790b810e3498984e8..31da83ad5137ebbfef97353ac4d8d57a9671cb98 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -16,13 +16,14 @@
package android.view;
+import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsController.DEBUG;
import static android.view.InsetsState.ISIDE_BOTTOM;
-import static android.view.InsetsState.ISIDE_FLOATING;
import static android.view.InsetsState.ISIDE_LEFT;
import static android.view.InsetsState.ISIDE_RIGHT;
import static android.view.InsetsState.ISIDE_TOP;
+import static android.view.InsetsState.ITYPE_IME;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -74,6 +75,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
private final @InsetsType int mTypes;
private final InsetsAnimationControlCallbacks mController;
private final WindowInsetsAnimation mAnimation;
+ /** @see WindowInsetsAnimationController#hasZeroInsetsIme */
+ private final boolean mHasZeroInsetsIme;
private Insets mCurrentInsets;
private Insets mPendingInsets;
private float mPendingFraction;
@@ -84,6 +87,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
private float mPendingAlpha = 1.0f;
@VisibleForTesting(visibility = PACKAGE)
public boolean mReadyDispatched;
+ private Boolean mPerceptible;
@VisibleForTesting
public InsetsAnimationControlImpl(SparseArray controls, Rect frame,
@@ -102,6 +106,12 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
null /* typeSideMap */);
mShownInsets = calculateInsets(mInitialInsetsState, frame, controls, true /* shown */,
mTypeSideMap);
+ mHasZeroInsetsIme = mShownInsets.bottom == 0 && controlsInternalType(ITYPE_IME);
+ if (mHasZeroInsetsIme) {
+ // IME has shownInsets of ZERO, and can't map to a side by default.
+ // Map zero insets IME to bottom, making it a special case of bottom insets.
+ mTypeSideMap.put(ITYPE_IME, ISIDE_BOTTOM);
+ }
buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls);
mAnimation = new WindowInsetsAnimation(mTypes, interpolator,
@@ -112,6 +122,19 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
new Bounds(mHiddenInsets, mShownInsets));
}
+ private boolean calculatePerceptible(Insets currentInsets, float currentAlpha) {
+ return 100 * currentInsets.left >= 5 * (mShownInsets.left - mHiddenInsets.left)
+ && 100 * currentInsets.top >= 5 * (mShownInsets.top - mHiddenInsets.top)
+ && 100 * currentInsets.right >= 5 * (mShownInsets.right - mHiddenInsets.right)
+ && 100 * currentInsets.bottom >= 5 * (mShownInsets.bottom - mHiddenInsets.bottom)
+ && currentAlpha >= 0.5f;
+ }
+
+ @Override
+ public boolean hasZeroInsetsIme() {
+ return mHasZeroInsetsIme;
+ }
+
@Override
public Insets getHiddenStateInsets() {
return mHiddenInsets;
@@ -161,6 +184,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
mPendingInsets = sanitize(insets);
mPendingAlpha = sanitize(alpha);
mController.scheduleApplyChangeInsets(this);
+ boolean perceptible = calculatePerceptible(mPendingInsets, mPendingAlpha);
+ if (mPerceptible == null || perceptible != mPerceptible) {
+ mController.reportPerceptible(mTypes, perceptible);
+ mPerceptible = perceptible;
+ }
}
@VisibleForTesting
@@ -182,8 +210,6 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
params, state, mPendingAlpha);
updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, mShownInsets.bottom,
mPendingInsets.bottom, params, state, mPendingAlpha);
- updateLeashesForSide(ISIDE_FLOATING, 0 /* offset */, 0 /* inset */, 0 /* maxInset */,
- params, state, mPendingAlpha);
mController.applySurfaceParams(params.toArray(new SurfaceParams[params.size()]));
mCurrentInsets = mPendingInsets;
@@ -290,6 +316,9 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
if (insets == null) {
insets = getCurrentInsets();
}
+ if (hasZeroInsetsIme()) {
+ return insets;
+ }
return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
}
@@ -313,17 +342,19 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
mTmpFrame.set(source.getFrame());
addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame);
- state.getSource(source.getType()).setVisible(side == ISIDE_FLOATING || inset != 0);
+ final boolean visible = mHasZeroInsetsIme && side == ISIDE_BOTTOM
+ ? (mAnimationType == ANIMATION_TYPE_SHOW ? true : !mFinished)
+ : inset != 0;
+
+ state.getSource(source.getType()).setVisible(visible);
state.getSource(source.getType()).setFrame(mTmpFrame);
// If the system is controlling the insets source, the leash can be null.
if (leash != null) {
SurfaceParams params = new SurfaceParams.Builder(leash)
- .withAlpha(side == ISIDE_FLOATING ? 1 : alpha)
+ .withAlpha(alpha)
.withMatrix(mTmpMatrix)
- .withVisibility(side == ISIDE_FLOATING
- ? mShownOnFinish
- : inset != 0 /* visible */)
+ .withVisibility(visible)
.build();
surfaceParams.add(params);
}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 0e71b7643b7d9bb1b599fed15af18a642d5d1a15..123604489da498468c5e5ce9997eedc1d3dfa597 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -90,6 +90,11 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
// Since we don't push the SurfaceParams to the RT we can release directly
sc.release();
}
+
+ @Override
+ public void reportPerceptible(int types, boolean perceptible) {
+ mMainThreadHandler.post(() -> mOuterCallbacks.reportPerceptible(types, perceptible));
+ }
};
@UiThread
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index ef9edc6c074171ea96f809706a19c30bc352d690..a679b3740fd90ab177dfc2564faca30d880a597f 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -35,6 +35,7 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Trace;
import android.util.ArraySet;
import android.util.Log;
@@ -162,6 +163,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
*/
@Nullable
String getRootViewTitle();
+
+ /** @see ViewRootImpl#dipToPx */
+ int dipToPx(int dips);
+
+ /**
+ * @return token associated with the host, if it has one.
+ */
+ @Nullable
+ IBinder getWindowToken();
}
private static final String TAG = "InsetsController";
@@ -254,12 +264,17 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
public static class InternalAnimationControlListener
implements WindowInsetsAnimationControlListener {
+ /** The amount IME will move up/down when animating in floating mode. */
+ protected static final int FLOATING_IME_BOTTOM_INSET = -80;
+
private WindowInsetsAnimationController mController;
private ValueAnimator mAnimator;
private final boolean mShow;
private final boolean mHasAnimationCallbacks;
private final @InsetsType int mRequestedTypes;
private final long mDurationMs;
+ private final boolean mDisable;
+ private final int mFloatingImeBottomInset;
private ThreadLocal mSfAnimationHandlerThreadLocal =
new ThreadLocal() {
@@ -272,11 +287,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
};
public InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks,
- int requestedTypes) {
+ int requestedTypes, boolean disable, int floatingImeBottomInset) {
mShow = show;
mHasAnimationCallbacks = hasAnimationCallbacks;
mRequestedTypes = requestedTypes;
mDurationMs = calculateDurationMs();
+ mDisable = disable;
+ mFloatingImeBottomInset = floatingImeBottomInset;
}
@Override
@@ -284,15 +301,26 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
mController = controller;
if (DEBUG) Log.d(TAG, "default animation onReady types: " + types);
+ if (mDisable) {
+ onAnimationFinish();
+ return;
+ }
mAnimator = ValueAnimator.ofFloat(0f, 1f);
mAnimator.setDuration(mDurationMs);
mAnimator.setInterpolator(new LinearInterpolator());
+ Insets hiddenInsets = controller.getHiddenStateInsets();
+ // IME with zero insets is a special case: it will animate-in from offscreen and end
+ // with final insets of zero and vice-versa.
+ hiddenInsets = controller.hasZeroInsetsIme()
+ ? Insets.of(hiddenInsets.left, hiddenInsets.top, hiddenInsets.right,
+ mFloatingImeBottomInset)
+ : hiddenInsets;
Insets start = mShow
- ? controller.getHiddenStateInsets()
+ ? hiddenInsets
: controller.getShownStateInsets();
Insets end = mShow
? controller.getShownStateInsets()
- : controller.getHiddenStateInsets();
+ : hiddenInsets;
Interpolator insetsInterpolator = getInterpolator();
Interpolator alphaInterpolator = getAlphaInterpolator();
mAnimator.addUpdateListener(animation -> {
@@ -477,6 +505,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private DisplayCutout mLastDisplayCutout;
private boolean mStartingAnimation;
private int mCaptionInsetsHeight = 0;
+ private boolean mAnimationsDisabled;
private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
private final ArrayList mControllableInsetsChangedListeners
@@ -485,6 +514,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
/** Set of inset types for which an animation was started since last resetting this field */
private @InsetsType int mLastStartedAnimTypes;
+ /** Set of inset types which cannot be controlled by the user animation */
+ private @InsetsType int mDisabledUserAnimationInsetsTypes;
+
+ private Runnable mInvokeControllableInsetsChangedListeners =
+ this::invokeControllableInsetsChangedListeners;
+
public InsetsController(Host host) {
this(host, (controller, type) -> {
if (type == ITYPE_IME) {
@@ -575,21 +610,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
public boolean onStateChanged(InsetsState state) {
- boolean localStateChanged = !mState.equals(state, true /* excludingCaptionInsets */)
+ boolean stateChanged = !mState.equals(state, true /* excludingCaptionInsets */,
+ false /* excludeInvisibleIme */)
|| !captionInsetsUnchanged();
- if (!localStateChanged && mLastDispatchedState.equals(state)) {
+ if (!stateChanged && mLastDispatchedState.equals(state)) {
return false;
}
if (DEBUG) Log.d(TAG, "onStateChanged: " + state);
updateState(state);
+
+ boolean localStateChanged = !mState.equals(mLastDispatchedState,
+ true /* excludingCaptionInsets */, true /* excludeInvisibleIme */);
mLastDispatchedState.set(state, true /* copySources */);
+
applyLocalVisibilityOverride();
if (localStateChanged) {
- if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
+ if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged, send state to WM: " + mState);
mHost.notifyInsetsChanged();
- }
- if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */)) {
- if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState);
updateRequestedState();
}
return true;
@@ -597,20 +634,57 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private void updateState(InsetsState newState) {
mState.setDisplayFrame(newState.getDisplayFrame());
- for (int i = newState.getSourcesCount() - 1; i >= 0; i--) {
- InsetsSource source = newState.sourceAt(i);
- getSourceConsumer(source.getType()).updateSource(source);
+ @InsetsType int disabledUserAnimationTypes = 0;
+ @InsetsType int[] cancelledUserAnimationTypes = {0};
+ for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
+ InsetsSource source = newState.peekSource(type);
+ if (source == null) continue;
+ @AnimationType int animationType = getAnimationType(type);
+ if (!source.isUserControllable()) {
+ @InsetsType int insetsType = toPublicType(type);
+ // The user animation is not allowed when visible frame is empty.
+ disabledUserAnimationTypes |= insetsType;
+ if (animationType == ANIMATION_TYPE_USER) {
+ // Existing user animation needs to be cancelled.
+ animationType = ANIMATION_TYPE_NONE;
+ cancelledUserAnimationTypes[0] |= insetsType;
+ }
+ }
+ getSourceConsumer(type).updateSource(source, animationType);
}
- for (int i = mState.getSourcesCount() - 1; i >= 0; i--) {
- InsetsSource source = mState.sourceAt(i);
- if (newState.peekSource(source.getType()) == null) {
- mState.removeSource(source.getType());
+ for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
+ InsetsSource source = mState.peekSource(type);
+ if (source == null) continue;
+ if (newState.peekSource(type) == null) {
+ mState.removeSource(type);
}
}
if (mCaptionInsetsHeight != 0) {
mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top,
mFrame.right, mFrame.top + mCaptionInsetsHeight));
}
+
+ updateDisabledUserAnimationTypes(disabledUserAnimationTypes);
+
+ if (cancelledUserAnimationTypes[0] != 0) {
+ mHandler.post(() -> show(cancelledUserAnimationTypes[0]));
+ }
+ }
+
+ private void updateDisabledUserAnimationTypes(@InsetsType int disabledUserAnimationTypes) {
+ @InsetsType int diff = mDisabledUserAnimationInsetsTypes ^ disabledUserAnimationTypes;
+ if (diff != 0) {
+ for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+ InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+ if (consumer.getControl() != null
+ && (toPublicType(consumer.getType()) & diff) != 0) {
+ mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners);
+ mHandler.post(mInvokeControllableInsetsChangedListeners);
+ break;
+ }
+ }
+ mDisabledUserAnimationInsetsTypes = disabledUserAnimationTypes;
+ }
}
private boolean captionInsetsUnchanged() {
@@ -663,7 +737,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
}
- final boolean hasControl = mTmpControlArray.size() > 0;
+ boolean requestedStateStale = false;
final int[] showTypes = new int[1];
final int[] hideTypes = new int[1];
@@ -680,9 +754,26 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// Ensure to create source consumers if not available yet.
for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
final InsetsSourceControl control = mTmpControlArray.valueAt(i);
- InsetsSourceConsumer consumer = getSourceConsumer(control.getType());
+ final @InternalInsetsType int type = control.getType();
+ final InsetsSourceConsumer consumer = getSourceConsumer(type);
consumer.setControl(control, showTypes, hideTypes);
+ if (!requestedStateStale) {
+ final boolean requestedVisible = consumer.isRequestedVisible();
+
+ // We might have changed our requested visibilities while we don't have the control,
+ // so we need to update our requested state once we have control. Otherwise, our
+ // requested state at the server side might be incorrect.
+ final boolean requestedVisibilityChanged =
+ requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type);
+
+ // The IME client visibility will be reset by insets source provider while updating
+ // control, so if IME is requested visible, we need to send the request to server.
+ final boolean imeRequestedVisible = type == ITYPE_IME && requestedVisible;
+
+ requestedStateStale = requestedVisibilityChanged || imeRequestedVisible;
+ }
+
}
mTmpControlArray.clear();
@@ -698,10 +789,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (hideTypes[0] != 0) {
applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
}
- if (hasControl && mRequestedState.getSourcesCount() > 0) {
- // We might have changed our requested visibilities while we don't have the control,
- // so we need to update our requested state once we have control. Otherwise, our
- // requested state at the server side might be incorrect.
+ if (requestedStateStale) {
updateRequestedState();
}
}
@@ -791,7 +879,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
WindowInsetsAnimationControlListener listener,
boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
@AnimationType int animationType) {
- if (!checkDisplayFramesForControlling()) {
+ if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0) {
listener.onCancelled(null);
return;
}
@@ -801,13 +889,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
false /* useInsetsAnimationThread */);
}
- private boolean checkDisplayFramesForControlling() {
-
- // If the frame of our window doesn't span the entire display, the control API makes very
- // little sense, as we don't deal with negative insets. So just cancel immediately.
- return mState.getDisplayFrame().equals(mFrame);
- }
-
private void controlAnimationUnchecked(@InsetsType int types,
@Nullable CancellationSignal cancellationSignal,
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
@@ -821,6 +902,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
+ " while an existing " + Type.toString(mTypesBeingCancelled)
+ " is being cancelled.");
}
+ if (animationType == ANIMATION_TYPE_USER) {
+ final @InsetsType int disabledTypes = types & mDisabledUserAnimationInsetsTypes;
+ if (DEBUG) Log.d(TAG, "user animation disabled types: " + disabledTypes);
+ types &= ~mDisabledUserAnimationInsetsTypes;
+
+ if (fromIme && (disabledTypes & ime()) != 0
+ && !mState.getSource(ITYPE_IME).isVisible()) {
+ // We've requested IMM to show IME, but the IME is not controllable. We need to
+ // cancel the request.
+ getSourceConsumer(ITYPE_IME).hide(true, animationType);
+ }
+ }
if (types == 0) {
// nothing to animate.
listener.onCancelled(null);
@@ -1168,8 +1261,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks();
- final InternalAnimationControlListener listener =
- new InternalAnimationControlListener(show, hasAnimationCallbacks, types);
+ final InternalAnimationControlListener listener = new InternalAnimationControlListener(
+ show, hasAnimationCallbacks, types, mAnimationsDisabled,
+ mHost.dipToPx(InternalAnimationControlListener.FLOATING_IME_BOTTOM_INSET));
// Show/hide animations always need to be relative to the display frame, in order that shown
// and hidden state insets are correct.
@@ -1284,24 +1378,28 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return mHost.getSystemBarsBehavior();
}
+ @Override
+ public void setAnimationsDisabled(boolean disable) {
+ mAnimationsDisabled = disable;
+ }
+
private @InsetsType int calculateControllableTypes() {
- if (!checkDisplayFramesForControlling()) {
- return 0;
- }
@InsetsType int result = 0;
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
- if (consumer.getControl() != null) {
+ InsetsSource source = mState.peekSource(consumer.mType);
+ if (consumer.getControl() != null && source != null && source.isUserControllable()) {
result |= toPublicType(consumer.mType);
}
}
- return result;
+ return result & ~mState.calculateUncontrollableInsetsFromFrame(mFrame);
}
/**
* @return The types that are now animating due to a listener invoking control/show/hide
*/
private @InsetsType int invokeControllableInsetsChangedListeners() {
+ mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners);
mLastStartedAnimTypes = 0;
@InsetsType int types = calculateControllableTypes();
int size = mControllableInsetsChangedListeners.size();
@@ -1331,6 +1429,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
mHost.releaseSurfaceControlFromRt(sc);
}
+ @Override
+ public void reportPerceptible(int types, boolean perceptible) {
+ final ArraySet internalTypes = toInternalType(types);
+ final int size = mSourceConsumers.size();
+ for (int i = 0; i < size; i++) {
+ final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+ if (internalTypes.contains(consumer.getType())) {
+ consumer.onPerceptible(perceptible);
+ }
+ }
+ }
+
Host getHost() {
return mHost;
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index b0158467a17b399526ea32406bcea5d22b7dde89..dbf75705c073fd949c7fd706112c61e63dff7ba5 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -92,6 +92,11 @@ public class InsetsSource implements Parcelable {
return mVisible;
}
+ boolean isUserControllable() {
+ // If mVisibleFrame is null, it will be the same area as mFrame.
+ return mVisibleFrame == null || !mVisibleFrame.isEmpty();
+ }
+
/**
* Calculates the insets this source will cause to a client window.
*
@@ -191,6 +196,14 @@ public class InsetsSource implements Parcelable {
@Override
public boolean equals(Object o) {
+ return equals(o, false);
+ }
+
+ /**
+ * @param excludeInvisibleImeFrames If {@link InsetsState#ITYPE_IME} frames should be ignored
+ * when IME is not visible.
+ */
+ public boolean equals(Object o, boolean excludeInvisibleImeFrames) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -198,6 +211,7 @@ public class InsetsSource implements Parcelable {
if (mType != that.mType) return false;
if (mVisible != that.mVisible) return false;
+ if (excludeInvisibleImeFrames && !mVisible && mType == ITYPE_IME) return true;
if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false;
return mFrame.equals(that.mFrame);
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 3869484468ae937ae8cb858e19571a571e10084a..700dc66fab556becedf5277a9d6bd570dac27ebd 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -18,10 +18,12 @@ package android.view;
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.AnimationType;
-import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsController.DEBUG;
+import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsState.toPublicType;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Rect;
@@ -151,6 +153,9 @@ public class InsetsSourceConsumer {
if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) {
applyHiddenToControl();
}
+ if (!requestedVisible && !mIsAnimationPending) {
+ removeSurface();
+ }
}
}
if (lastControl != null) {
@@ -219,9 +224,10 @@ public class InsetsSourceConsumer {
final boolean hasControl = mSourceControl != null;
// We still need to let the legacy app know the visibility change even if we don't have the
- // control.
+ // control. If we don't have the source, we don't change the requested visibility for making
+ // the callback behavior compatible.
mController.updateCompatSysUiVisibility(
- mType, hasControl ? mRequestedVisible : isVisible, hasControl);
+ mType, (hasControl || source == null) ? mRequestedVisible : isVisible, hasControl);
// If we don't have control, we are not able to change the visibility.
if (!hasControl) {
@@ -257,6 +263,15 @@ public class InsetsSourceConsumer {
return ShowResult.SHOW_IMMEDIATELY;
}
+ /**
+ * Reports that this source's perceptibility has changed
+ *
+ * @param perceptible true if the source is perceptible, false otherwise.
+ * @see InsetsAnimationControlCallbacks#reportPerceptible
+ */
+ public void onPerceptible(boolean perceptible) {
+ }
+
/**
* Notify listeners that window is now hidden.
*/
@@ -271,16 +286,19 @@ public class InsetsSourceConsumer {
// no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
}
- void updateSource(InsetsSource newSource) {
+ @VisibleForTesting(visibility = PACKAGE)
+ public void updateSource(InsetsSource newSource, @AnimationType int animationType) {
InsetsSource source = mState.peekSource(mType);
- if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE
+ if (source == null || animationType == ANIMATION_TYPE_NONE
|| source.getFrame().equals(newSource.getFrame())) {
+ mPendingFrame = null;
+ mPendingVisibleFrame = null;
mState.addSource(newSource);
return;
}
// Frame is changing while animating. Keep note of the new frame but keep existing frame
- // until animaition is finished.
+ // until animation is finished.
newSource = new InsetsSource(newSource);
mPendingFrame = new Rect(newSource.getFrame());
mPendingVisibleFrame = newSource.getVisibleFrame() != null
@@ -292,7 +310,8 @@ public class InsetsSourceConsumer {
if (DEBUG) Log.d(TAG, "updateSource: " + newSource);
}
- boolean notifyAnimationFinished() {
+ @VisibleForTesting(visibility = PACKAGE)
+ public boolean notifyAnimationFinished() {
if (mPendingFrame != null) {
InsetsSource source = mState.getSource(mType);
source.setFrame(mPendingFrame);
@@ -332,5 +351,6 @@ public class InsetsSourceConsumer {
t.hide(mSourceControl.getLeash());
}
t.apply();
+ onPerceptible(mRequestedVisible);
}
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 2c2ecd504519b0c120e818f4aa1065349ef322a6..51b49214387a01ff99311d0520d9a86fb23b1907 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -45,7 +45,7 @@ public class InsetsSourceControl implements Parcelable {
public InsetsSourceControl(InsetsSourceControl other) {
mType = other.mType;
if (other.mLeash != null) {
- mLeash = new SurfaceControl(other.mLeash);
+ mLeash = new SurfaceControl(other.mLeash, "InsetsSourceControl");
} else {
mLeash = null;
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 3822ee59b4aad71f0870383886ba9e302796a481..91e7591193f17e618db43a9dd1c3e4107bf7cde0 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -50,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.Objects;
import java.util.StringJoiner;
@@ -117,6 +118,7 @@ public class InsetsState implements Parcelable {
public static final int ITYPE_EXTRA_NAVIGATION_BAR = 15;
static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
+ public static final int SIZE = LAST_TYPE + 1;
// Derived types
@@ -140,7 +142,7 @@ public class InsetsState implements Parcelable {
static final int ISIDE_FLOATING = 4;
static final int ISIDE_UNKNOWN = 5;
- private final ArrayMap mSources = new ArrayMap<>();
+ private InsetsSource[] mSources = new InsetsSource[SIZE];
/**
* The frame of the display these sources are relative to.
@@ -177,7 +179,7 @@ public class InsetsState implements Parcelable {
final Rect relativeFrame = new Rect(frame);
final Rect relativeFrameMax = new Rect(frame);
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
- InsetsSource source = mSources.get(type);
+ InsetsSource source = mSources[type];
if (source == null) {
int index = indexOf(toPublicType(type));
if (typeInsetsMap[index] == null) {
@@ -227,7 +229,7 @@ public class InsetsState implements Parcelable {
public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
Insets insets = Insets.NONE;
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
- InsetsSource source = mSources.get(type);
+ InsetsSource source = mSources[type];
if (source == null) {
continue;
}
@@ -245,6 +247,44 @@ public class InsetsState implements Parcelable {
return insets.toRect();
}
+ /**
+ * Calculate which insets *cannot* be controlled, because the frame does not cover the
+ * respective side of the inset.
+ *
+ * If the frame of our window doesn't cover the entire inset, the control API makes very
+ * little sense, as we don't deal with negative insets.
+ */
+ @InsetsType
+ public int calculateUncontrollableInsetsFromFrame(Rect frame) {
+ int blocked = 0;
+ for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ InsetsSource source = mSources[type];
+ if (source == null) {
+ continue;
+ }
+ if (!canControlSide(frame, getInsetSide(
+ source.calculateInsets(frame, true /* ignoreVisibility */)))) {
+ blocked |= toPublicType(type);
+ }
+ }
+ return blocked;
+ }
+
+ private boolean canControlSide(Rect frame, int side) {
+ switch (side) {
+ case ISIDE_LEFT:
+ case ISIDE_RIGHT:
+ return frame.left == mDisplayFrame.left && frame.right == mDisplayFrame.right;
+ case ISIDE_TOP:
+ case ISIDE_BOTTOM:
+ return frame.top == mDisplayFrame.top && frame.bottom == mDisplayFrame.bottom;
+ case ISIDE_FLOATING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap,
@Nullable boolean[] typeVisibilityMap) {
@@ -312,11 +352,39 @@ public class InsetsState implements Parcelable {
}
public InsetsSource getSource(@InternalInsetsType int type) {
- return mSources.computeIfAbsent(type, InsetsSource::new);
+ InsetsSource source = mSources[type];
+ if (source != null) {
+ return source;
+ }
+ source = new InsetsSource(type);
+ mSources[type] = source;
+ return source;
}
public @Nullable InsetsSource peekSource(@InternalInsetsType int type) {
- return mSources.get(type);
+ return mSources[type];
+ }
+
+ public boolean hasSources() {
+ for (int i = 0; i < SIZE; i++) {
+ if (mSources[i] != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the source visibility or the default visibility if the source doesn't exist. This is
+ * useful if when treating this object as a request.
+ *
+ * @param type The {@link InternalInsetsType} to query.
+ * @return {@code true} if the source is visible or the type is default visible and the source
+ * doesn't exist.
+ */
+ public boolean getSourceOrDefaultVisibility(@InternalInsetsType int type) {
+ final InsetsSource source = mSources[type];
+ return source != null ? source.isVisible() : getDefaultVisibility(type);
}
public void setDisplayFrame(Rect frame) {
@@ -334,7 +402,7 @@ public class InsetsState implements Parcelable {
* @param type The {@link InternalInsetsType} of the source to remove
*/
public void removeSource(@InternalInsetsType int type) {
- mSources.remove(type);
+ mSources[type] = null;
}
/**
@@ -344,53 +412,32 @@ public class InsetsState implements Parcelable {
* @param visible {@code true} for visible
*/
public void setSourceVisible(@InternalInsetsType int type, boolean visible) {
- InsetsSource source = mSources.get(type);
+ InsetsSource source = mSources[type];
if (source != null) {
source.setVisible(visible);
}
}
- /**
- * A shortcut for setting the visibility of the source.
- *
- * @param type The {@link InternalInsetsType} of the source to set the visibility
- * @param referenceState The {@link InsetsState} for reference
- */
- public void setSourceVisible(@InternalInsetsType int type, InsetsState referenceState) {
- InsetsSource source = mSources.get(type);
- InsetsSource referenceSource = referenceState.mSources.get(type);
- if (source != null && referenceSource != null) {
- source.setVisible(referenceSource.isVisible());
- }
- }
-
public void set(InsetsState other) {
set(other, false /* copySources */);
}
public void set(InsetsState other, boolean copySources) {
mDisplayFrame.set(other.mDisplayFrame);
- mSources.clear();
if (copySources) {
- for (int i = 0; i < other.mSources.size(); i++) {
- InsetsSource source = other.mSources.valueAt(i);
- mSources.put(source.getType(), new InsetsSource(source));
+ for (int i = 0; i < SIZE; i++) {
+ InsetsSource source = other.mSources[i];
+ mSources[i] = source != null ? new InsetsSource(source) : null;
}
} else {
- mSources.putAll(other.mSources);
+ for (int i = 0; i < SIZE; i++) {
+ mSources[i] = other.mSources[i];
+ }
}
}
public void addSource(InsetsSource source) {
- mSources.put(source.getType(), source);
- }
-
- public int getSourcesCount() {
- return mSources.size();
- }
-
- public InsetsSource sourceAt(int index) {
- return mSources.valueAt(index);
+ mSources[source.getType()] = source;
}
public static @InternalInsetsType ArraySet toInternalType(@InsetsType int types) {
@@ -452,7 +499,7 @@ public class InsetsState implements Parcelable {
}
}
- public static boolean getDefaultVisibility(@InsetsType int type) {
+ public static boolean getDefaultVisibility(@InternalInsetsType int type) {
return type != ITYPE_IME;
}
@@ -471,8 +518,10 @@ public class InsetsState implements Parcelable {
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "InsetsState");
- for (int i = mSources.size() - 1; i >= 0; i--) {
- mSources.valueAt(i).dump(prefix + " ", pw);
+ for (int i = 0; i < SIZE; i++) {
+ InsetsSource source = mSources[i];
+ if (source == null) continue;
+ source.dump(prefix + " ", pw);
}
}
@@ -517,7 +566,7 @@ public class InsetsState implements Parcelable {
@Override
public boolean equals(Object o) {
- return equals(o, false);
+ return equals(o, false, false);
}
/**
@@ -526,10 +575,13 @@ public class InsetsState implements Parcelable {
* excluded.
* @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but
* ignore the caption insets source value.
+ * @param excludeInvisibleImeFrames If {@link #ITYPE_IME} frames should be ignored when IME is
+ * not visible.
* @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
*/
@VisibleForTesting
- public boolean equals(Object o, boolean excludingCaptionInsets) {
+ public boolean equals(Object o, boolean excludingCaptionInsets,
+ boolean excludeInvisibleImeFrames) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
@@ -538,29 +590,19 @@ public class InsetsState implements Parcelable {
if (!mDisplayFrame.equals(state.mDisplayFrame)) {
return false;
}
- int size = mSources.size();
- int otherSize = state.mSources.size();
- if (excludingCaptionInsets) {
- if (mSources.get(ITYPE_CAPTION_BAR) != null) {
- size--;
- }
- if (state.mSources.get(ITYPE_CAPTION_BAR) != null) {
- otherSize--;
- }
- }
- if (size != otherSize) {
- return false;
- }
- for (int i = mSources.size() - 1; i >= 0; i--) {
- InsetsSource source = mSources.valueAt(i);
+ for (int i = 0; i < SIZE; i++) {
if (excludingCaptionInsets) {
- if (source.getType() == ITYPE_CAPTION_BAR) continue;
+ if (i == ITYPE_CAPTION_BAR) continue;
+ }
+ InsetsSource source = mSources[i];
+ InsetsSource otherSource = state.mSources[i];
+ if (source == null && otherSource == null) {
+ continue;
}
- InsetsSource otherSource = state.mSources.get(source.getType());
- if (otherSource == null) {
+ if (source != null && otherSource == null || source == null && otherSource != null) {
return false;
}
- if (!otherSource.equals(source)) {
+ if (!otherSource.equals(source, excludeInvisibleImeFrames)) {
return false;
}
}
@@ -569,7 +611,7 @@ public class InsetsState implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mDisplayFrame, mSources);
+ return Objects.hash(mDisplayFrame, Arrays.hashCode(mSources));
}
public InsetsState(Parcel in) {
@@ -584,10 +626,7 @@ public class InsetsState implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mDisplayFrame, flags);
- dest.writeInt(mSources.size());
- for (int i = 0; i < mSources.size(); i++) {
- dest.writeParcelable(mSources.valueAt(i), flags);
- }
+ dest.writeParcelableArray(mSources, 0);
}
public static final @android.annotation.NonNull Creator CREATOR = new Creator() {
@@ -602,19 +641,15 @@ public class InsetsState implements Parcelable {
};
public void readFromParcel(Parcel in) {
- mSources.clear();
mDisplayFrame.set(in.readParcelable(null /* loader */));
- final int size = in.readInt();
- for (int i = 0; i < size; i++) {
- final InsetsSource source = in.readParcelable(null /* loader */);
- mSources.put(source.getType(), source);
- }
+ mSources = in.readParcelableArray(null, InsetsSource.class);
}
@Override
public String toString() {
StringJoiner joiner = new StringJoiner(", ");
- for (InsetsSource source : mSources.values()) {
+ for (int i = 0; i < SIZE; i++) {
+ InsetsSource source = mSources[i];
if (source != null) {
joiner.add(source.toString());
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 19eff72ca814264c6777edc3915343a995e61a85..51b0c6b59f3c1dc32799579a99bbb828964484f2 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -486,6 +486,21 @@ public final class MotionEvent extends InputEvent implements Parcelable {
*/
public static final int FLAG_TAINTED = 0x80000000;
+ /**
+ * Private flag indicating that this event was synthesized by the system and should be delivered
+ * to the accessibility focused view first. When being dispatched such an event is not handled
+ * by predecessors of the accessibility focused view and after the event reaches that view the
+ * flag is cleared and normal event dispatch is performed. This ensures that the platform can
+ * click on any view that has accessibility focus which is semantically equivalent to asking the
+ * view to perform a click accessibility action but more generic as views not implementing click
+ * action correctly can still be activated.
+ *
+ * @hide
+ * @see #isTargetAccessibilityFocus()
+ * @see #setTargetAccessibilityFocus(boolean)
+ */
+ public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000;
+
/**
* Flag indicating the motion event intersected the top edge of the screen.
*/
@@ -2139,6 +2154,20 @@ public final class MotionEvent extends InputEvent implements Parcelable {
nativeSetFlags(mNativePtr, tainted ? flags | FLAG_TAINTED : flags & ~FLAG_TAINTED);
}
+ /** @hide */
+ public boolean isTargetAccessibilityFocus() {
+ final int flags = getFlags();
+ return (flags & FLAG_TARGET_ACCESSIBILITY_FOCUS) != 0;
+ }
+
+ /** @hide */
+ public void setTargetAccessibilityFocus(boolean targetsFocus) {
+ final int flags = getFlags();
+ nativeSetFlags(mNativePtr, targetsFocus
+ ? flags | FLAG_TARGET_ACCESSIBILITY_FOCUS
+ : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS);
+ }
+
/** @hide */
public final boolean isHoverExitPending() {
final int flags = getFlags();
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index 0283ada0dd40d8901087ca5a8ddc2179bef8d031..c018d1cf17820fea344c999f3771fb56424c833d 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -38,6 +38,7 @@ public class PendingInsetsController implements WindowInsetsController {
private @Appearance int mAppearance;
private @Appearance int mAppearanceMask;
private @Behavior int mBehavior = KEEP_BEHAVIOR;
+ private boolean mAnimationsDisabled;
private final InsetsState mDummyState = new InsetsState();
private InsetsController mReplayedInsetsController;
private ArrayList mControllableInsetsChangedListeners
@@ -102,6 +103,15 @@ public class PendingInsetsController implements WindowInsetsController {
return mBehavior;
}
+ @Override
+ public void setAnimationsDisabled(boolean disable) {
+ if (mReplayedInsetsController != null) {
+ mReplayedInsetsController.setAnimationsDisabled(disable);
+ } else {
+ mAnimationsDisabled = disable;
+ }
+ }
+
@Override
public InsetsState getState() {
return mDummyState;
@@ -151,6 +161,9 @@ public class PendingInsetsController implements WindowInsetsController {
if (mCaptionInsetsHeight != 0) {
controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
}
+ if (mAnimationsDisabled) {
+ controller.setAnimationsDisabled(true);
+ }
int size = mRequests.size();
for (int i = 0; i < size; i++) {
mRequests.get(i).replay(controller);
@@ -167,6 +180,7 @@ public class PendingInsetsController implements WindowInsetsController {
mBehavior = KEEP_BEHAVIOR;
mAppearance = 0;
mAppearanceMask = 0;
+ mAnimationsDisabled = false;
// After replaying, we forward everything directly to the replayed instance.
mReplayedInsetsController = controller;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9109f50247e0aea567c23770d1a52c86e431e7fc..87b2f4b46df7ad09261cf5e0274fc1140dabaaf1 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -49,6 +49,7 @@ import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Trace;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseIntArray;
@@ -62,8 +63,10 @@ import dalvik.system.CloseGuard;
import libcore.util.NativeAllocationRegistry;
import java.io.Closeable;
+import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.ArrayList;
import java.util.Objects;
/**
@@ -102,6 +105,8 @@ public final class SurfaceControl implements Parcelable {
long otherTransactionObj);
private static native void nativeSetAnimationTransaction(long transactionObj);
private static native void nativeSetEarlyWakeup(long transactionObj);
+ private static native void nativeSetEarlyWakeupStart(long transactionObj);
+ private static native void nativeSetEarlyWakeupEnd(long transactionObj);
private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
private static native void nativeSetRelativeLayer(long transactionObj, long nativeObject,
@@ -223,25 +228,86 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject,
int transformHint);
+ @Nullable
+ @GuardedBy("mLock")
+ private ArrayList mReparentListeners;
+
+ /**
+ * Listener to observe surface reparenting.
+ *
+ * @hide
+ */
+ public interface OnReparentListener {
+
+ /**
+ * Callback for reparenting surfaces.
+ *
+ * Important: You should only interact with the provided surface control
+ * only if you have a contract with its owner to avoid them closing it
+ * under you or vise versa.
+ *
+ * @param transaction The transaction that would commit reparenting.
+ * @param parent The future parent surface.
+ */
+ void onReparent(@NonNull Transaction transaction, @Nullable SurfaceControl parent);
+ }
+
private final CloseGuard mCloseGuard = CloseGuard.get();
private String mName;
- /**
+
+ /**
* @hide
*/
public long mNativeObject;
private long mNativeHandle;
- private Throwable mReleaseStack = null;
- // TODO: Move this to native.
- private final Object mSizeLock = new Object();
- @GuardedBy("mSizeLock")
+ // TODO: Move width/height to native and fix locking through out.
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
private int mWidth;
- @GuardedBy("mSizeLock")
+ @GuardedBy("mLock")
private int mHeight;
+ private WeakReference mLocalOwnerView;
+
static Transaction sGlobalTransaction;
static long sTransactionNestCount = 0;
+ /**
+ * Adds a reparenting listener.
+ *
+ * @param listener The listener.
+ * @return Whether listener was added.
+ *
+ * @hide
+ */
+ public boolean addOnReparentListener(@NonNull OnReparentListener listener) {
+ synchronized (mLock) {
+ if (mReparentListeners == null) {
+ mReparentListeners = new ArrayList<>(1);
+ }
+ return mReparentListeners.add(listener);
+ }
+ }
+
+ /**
+ * Removes a reparenting listener.
+ *
+ * @param listener The listener.
+ * @return Whether listener was removed.
+ *
+ * @hide
+ */
+ public boolean removeOnReparentListener(@NonNull OnReparentListener listener) {
+ synchronized (mLock) {
+ final boolean removed = mReparentListeners.remove(listener);
+ if (mReparentListeners.isEmpty()) {
+ mReparentListeners = null;
+ }
+ return removed;
+ }
+ }
+
/* flags used in constructor (keep in sync with ISurfaceComposerClient.h) */
/**
@@ -322,6 +388,14 @@ public final class SurfaceControl implements Parcelable {
*/
public static final int CURSOR_WINDOW = 0x00002000;
+ /**
+ * Surface creation flag: Indicates the effect layer will not have a color fill on
+ * creation.
+ *
+ * @hide
+ */
+ public static final int NO_COLOR_FILL = 0x00004000;
+
/**
* Surface creation flag: Creates a normal surface.
* This is the default.
@@ -425,32 +499,26 @@ public final class SurfaceControl implements Parcelable {
private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
private static final int INTERNAL_DATASPACE_SCRGB = 411107328;
- private void assignNativeObject(long nativeObject) {
+ private void assignNativeObject(long nativeObject, String callsite) {
if (mNativeObject != 0) {
release();
}
if (nativeObject != 0) {
- mCloseGuard.open("release");
+ mCloseGuard.openWithCallSite("release", callsite);
}
mNativeObject = nativeObject;
mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
- if (mNativeObject == 0) {
- if (Build.IS_DEBUGGABLE) {
- mReleaseStack = new Throwable("assigned zero nativeObject here");
- }
- } else {
- mReleaseStack = null;
- }
}
/**
* @hide
*/
- public void copyFrom(@NonNull SurfaceControl other) {
+ public void copyFrom(@NonNull SurfaceControl other, String callsite) {
mName = other.mName;
mWidth = other.mWidth;
mHeight = other.mHeight;
- assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject));
+ mLocalOwnerView = other.mLocalOwnerView;
+ assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite);
}
/**
@@ -548,8 +616,10 @@ public final class SurfaceControl implements Parcelable {
private int mHeight;
private int mFormat = PixelFormat.OPAQUE;
private String mName;
+ private WeakReference mLocalOwnerView;
private SurfaceControl mParent;
private SparseIntArray mMetadata;
+ private String mCallsite = "SurfaceControl.Builder";
/**
* Begin building a SurfaceControl with a given {@link SurfaceSession}.
@@ -577,12 +647,13 @@ public final class SurfaceControl implements Parcelable {
throw new IllegalStateException(
"width and height must be positive or unset");
}
- if ((mWidth > 0 || mHeight > 0) && (isColorLayerSet() || isContainerLayerSet())) {
+ if ((mWidth > 0 || mHeight > 0) && (isEffectLayer() || isContainerLayer())) {
throw new IllegalStateException(
"Only buffer layers can set a valid buffer size.");
}
return new SurfaceControl(
- mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata);
+ mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata,
+ mLocalOwnerView, mCallsite);
}
/**
@@ -596,6 +667,27 @@ public final class SurfaceControl implements Parcelable {
return this;
}
+ /**
+ * Set the local owner view for the surface. This view is only
+ * valid in the same process and is not transferred in an IPC.
+ *
+ * Note: This is used for cases where we want to know the view
+ * that manages the surface control while intercepting reparenting.
+ * A specific example is InlineContentView which exposes is surface
+ * control for reparenting as a way to implement clipping of several
+ * InlineContentView instances within a certain area.
+ *
+ * @param view The owner view.
+ * @return This builder.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setLocalOwnerView(@NonNull View view) {
+ mLocalOwnerView = new WeakReference<>(view);
+ return this;
+ }
+
/**
* Set the initial size of the controlled surface's buffers in pixels.
*
@@ -749,10 +841,27 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * Indicate whether a 'ColorLayer' is to be constructed.
+ * Indicate whether an 'EffectLayer' is to be constructed.
*
- * Color layers will not have an associated BufferQueue and will instead always render a
- * solid color (that is, solid before plane alpha). Currently that color is black.
+ * An effect layer behaves like a container layer by default but it can support
+ * color fill, shadows and/or blur. These layers will not have an associated buffer.
+ * When created, this layer has no effects set and will be transparent but the caller
+ * can render an effect by calling:
+ * - {@link Transaction#setColor(SurfaceControl, float[])}
+ * - {@link Transaction#setBackgroundBlurRadius(SurfaceControl, int)}
+ * - {@link Transaction#setShadowRadius(SurfaceControl, float)}
+ *
+ * @hide
+ */
+ public Builder setEffectLayer() {
+ mFlags |= NO_COLOR_FILL;
+ unsetBufferSize();
+ return setFlags(FX_SURFACE_EFFECT, FX_SURFACE_MASK);
+ }
+
+ /**
+ * A convenience function to create an effect layer with a default color fill
+ * applied to it. Currently that color is black.
*
* @hide
*/
@@ -761,7 +870,7 @@ public final class SurfaceControl implements Parcelable {
return setFlags(FX_SURFACE_EFFECT, FX_SURFACE_MASK);
}
- private boolean isColorLayerSet() {
+ private boolean isEffectLayer() {
return (mFlags & FX_SURFACE_EFFECT) == FX_SURFACE_EFFECT;
}
@@ -786,7 +895,7 @@ public final class SurfaceControl implements Parcelable {
return setFlags(FX_SURFACE_CONTAINER, FX_SURFACE_MASK);
}
- private boolean isContainerLayerSet() {
+ private boolean isContainerLayer() {
return (mFlags & FX_SURFACE_CONTAINER) == FX_SURFACE_CONTAINER;
}
@@ -802,6 +911,18 @@ public final class SurfaceControl implements Parcelable {
return this;
}
+ /**
+ * Sets the callsite this SurfaceControl is constructed from.
+ *
+ * @param callsite String uniquely identifying callsite that created this object. Used for
+ * leakage tracking.
+ * @hide
+ */
+ public Builder setCallsite(String callsite) {
+ mCallsite = callsite;
+ return this;
+ }
+
private Builder setFlags(int flags, int mask) {
mFlags = (mFlags & ~mask) | flags;
return this;
@@ -833,10 +954,13 @@ public final class SurfaceControl implements Parcelable {
* @param h The surface initial height.
* @param flags The surface creation flags.
* @param metadata Initial metadata.
+ * @param callsite String uniquely identifying callsite that created this object. Used for
+ * leakage tracking.
* @throws throws OutOfResourcesException If the SurfaceControl cannot be created.
*/
private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
- SurfaceControl parent, SparseIntArray metadata)
+ SurfaceControl parent, SparseIntArray metadata, WeakReference localOwnerView,
+ String callsite)
throws OutOfResourcesException, IllegalArgumentException {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
@@ -845,6 +969,7 @@ public final class SurfaceControl implements Parcelable {
mName = name;
mWidth = w;
mHeight = h;
+ mLocalOwnerView = localOwnerView;
Parcel metaParcel = Parcel.obtain();
try {
if (metadata != null && metadata.size() > 0) {
@@ -867,18 +992,20 @@ public final class SurfaceControl implements Parcelable {
"Couldn't allocate SurfaceControl native object");
}
mNativeHandle = nativeGetHandle(mNativeObject);
- mCloseGuard.open("release");
+ mCloseGuard.openWithCallSite("release", callsite);
}
/**
* Copy constructor. Creates a new native object pointing to the same surface as {@code other}.
*
* @param other The object to copy the surface from.
+ * @param callsite String uniquely identifying callsite that created this object. Used for
+ * leakage tracking.
* @hide
*/
@TestApi
- public SurfaceControl(@NonNull SurfaceControl other) {
- copyFrom(other);
+ public SurfaceControl(@NonNull SurfaceControl other, @NonNull String callsite) {
+ copyFrom(other, callsite);
}
private SurfaceControl(Parcel in) {
@@ -904,7 +1031,7 @@ public final class SurfaceControl implements Parcelable {
if (in.readInt() != 0) {
object = nativeReadFromParcel(in);
}
- assignNativeObject(object);
+ assignNativeObject(object, "readFromParcel");
}
@Override
@@ -999,21 +1126,10 @@ public final class SurfaceControl implements Parcelable {
nativeRelease(mNativeObject);
mNativeObject = 0;
mNativeHandle = 0;
- if (Build.IS_DEBUGGABLE) {
- mReleaseStack = new Throwable("released here");
- }
mCloseGuard.close();
}
}
- /**
- * Returns the call stack that assigned mNativeObject to zero.
- * @hide
- */
- public Throwable getReleaseStack() {
- return mReleaseStack;
- }
-
/**
* Disconnect any client still connected to the surface.
* @hide
@@ -1025,11 +1141,8 @@ public final class SurfaceControl implements Parcelable {
}
private void checkNotReleased() {
- if (mNativeObject == 0) {
- Log.wtf(TAG, "Invalid " + this + " caused by:", mReleaseStack);
- throw new NullPointerException(
- "mNativeObject of " + this + " is null. Have you called release() already?");
- }
+ if (mNativeObject == 0) throw new NullPointerException(
+ "Invalid " + this + ", mNativeObject is null. Have you called release() already?");
}
/**
@@ -1299,7 +1412,7 @@ public final class SurfaceControl implements Parcelable {
* @hide
*/
public int getWidth() {
- synchronized (mSizeLock) {
+ synchronized (mLock) {
return mWidth;
}
}
@@ -1308,11 +1421,22 @@ public final class SurfaceControl implements Parcelable {
* @hide
*/
public int getHeight() {
- synchronized (mSizeLock) {
+ synchronized (mLock) {
return mHeight;
}
}
+ /**
+ * Gets the local view that owns this surface.
+ *
+ * @return The owner view.
+ *
+ * @hide
+ */
+ public @Nullable View getLocalOwnerView() {
+ return (mLocalOwnerView != null) ? mLocalOwnerView.get() : null;
+ }
+
@Override
public String toString() {
return "Surface(name=" + mName + ")/@0x" +
@@ -2101,7 +2225,7 @@ public final class SurfaceControl implements Parcelable {
public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf) {
long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject);
SurfaceControl sc = new SurfaceControl();
- sc.assignNativeObject(nativeObj);
+ sc.assignNativeObject(nativeObj, "mirrorSurface");
return sc;
}
@@ -2157,6 +2281,9 @@ public final class SurfaceControl implements Parcelable {
public long mNativeObject;
private final ArrayMap mResizedSurfaces = new ArrayMap<>();
+ private final ArrayMap mReparentedSurfaces =
+ new ArrayMap<>();
+
Runnable mFreeNativeResources;
private static final float[] INVALID_COLOR = {-1, -1, -1};
@@ -2197,6 +2324,8 @@ public final class SurfaceControl implements Parcelable {
*/
@Override
public void close() {
+ mResizedSurfaces.clear();
+ mReparentedSurfaces.clear();
mFreeNativeResources.run();
mNativeObject = 0;
}
@@ -2207,6 +2336,7 @@ public final class SurfaceControl implements Parcelable {
*/
public void apply(boolean sync) {
applyResizedSurfaces();
+ notifyReparentedSurfaces();
nativeApplyTransaction(mNativeObject, sync);
}
@@ -2214,7 +2344,7 @@ public final class SurfaceControl implements Parcelable {
for (int i = mResizedSurfaces.size() - 1; i >= 0; i--) {
final Point size = mResizedSurfaces.valueAt(i);
final SurfaceControl surfaceControl = mResizedSurfaces.keyAt(i);
- synchronized (surfaceControl.mSizeLock) {
+ synchronized (surfaceControl.mLock) {
surfaceControl.mWidth = size.x;
surfaceControl.mHeight = size.y;
}
@@ -2222,6 +2352,22 @@ public final class SurfaceControl implements Parcelable {
mResizedSurfaces.clear();
}
+ private void notifyReparentedSurfaces() {
+ final int reparentCount = mReparentedSurfaces.size();
+ for (int i = reparentCount - 1; i >= 0; i--) {
+ final SurfaceControl child = mReparentedSurfaces.keyAt(i);
+ synchronized (child.mLock) {
+ final int listenerCount = (child.mReparentListeners != null)
+ ? child.mReparentListeners.size() : 0;
+ for (int j = 0; j < listenerCount; j++) {
+ final OnReparentListener listener = child.mReparentListeners.get(j);
+ listener.onReparent(this, mReparentedSurfaces.valueAt(i));
+ }
+ mReparentedSurfaces.removeAt(i);
+ }
+ }
+ }
+
/**
* Toggle the visibility of a given Layer and it's sub-tree.
*
@@ -2624,6 +2770,7 @@ public final class SurfaceControl implements Parcelable {
otherObject = newParent.mNativeObject;
}
nativeReparent(mNativeObject, sc.mNativeObject, otherObject);
+ mReparentedSurfaces.put(sc, newParent);
return this;
}
@@ -2772,6 +2919,8 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * @deprecated use {@link Transaction#setEarlyWakeupStart()}
+ *
* Indicate that SurfaceFlinger should wake up earlier than usual as a result of this
* transaction. This should be used when the caller thinks that the scene is complex enough
* that it's likely to hit GL composition, and thus, SurfaceFlinger needs to more time in
@@ -2780,11 +2929,35 @@ public final class SurfaceControl implements Parcelable {
* Corresponds to setting ISurfaceComposer::eEarlyWakeup
* @hide
*/
+ @Deprecated
public Transaction setEarlyWakeup() {
nativeSetEarlyWakeup(mNativeObject);
return this;
}
+ /**
+ * Provides a hint to SurfaceFlinger to change its offset so that SurfaceFlinger wakes up
+ * earlier to compose surfaces. The caller should use this as a hint to SurfaceFlinger
+ * when the scene is complex enough to use GPU composition. The hint will remain active
+ * until until the client calls {@link Transaction#setEarlyWakeupEnd}.
+ *
+ * @hide
+ */
+ public Transaction setEarlyWakeupStart() {
+ nativeSetEarlyWakeupStart(mNativeObject);
+ return this;
+ }
+
+ /**
+ * Removes the early wake up hint set by {@link Transaction#setEarlyWakeupStart}.
+ *
+ * @hide
+ */
+ public Transaction setEarlyWakeupEnd() {
+ nativeSetEarlyWakeupEnd(mNativeObject);
+ return this;
+ }
+
/**
* Sets an arbitrary piece of metadata on the surface. This is a helper for int data.
* @hide
@@ -2878,6 +3051,8 @@ public final class SurfaceControl implements Parcelable {
}
mResizedSurfaces.putAll(other.mResizedSurfaces);
other.mResizedSurfaces.clear();
+ mReparentedSurfaces.putAll(other.mReparentedSurfaces);
+ other.mReparentedSurfaces.clear();
nativeMergeTransaction(mNativeObject, other.mNativeObject);
return this;
}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 385078165e844a6c4f522da8b79eaebf130c6602..66ab3a32edfa644f3e188dff8e0f7f6a7faf91b7 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -167,9 +167,10 @@ public class SurfaceControlViewHost {
public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display,
@Nullable IBinder hostToken) {
mSurfaceControl = new SurfaceControl.Builder()
- .setContainerLayer()
- .setName("SurfaceControlViewHost")
- .build();
+ .setContainerLayer()
+ .setName("SurfaceControlViewHost")
+ .setCallsite("SurfaceControlViewHost")
+ .build();
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
mViewRoot = new ViewRootImpl(context, display, mWm);
@@ -177,6 +178,17 @@ public class SurfaceControlViewHost {
mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
+ /**
+ * @hide
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ // We aren't on the UI thread here so we need to pass false to
+ // doDie
+ mViewRoot.die(false /* immediate */);
+ }
+
+
/**
* Return a SurfacePackage for the root SurfaceControl of the embedded hierarchy.
* Rather than be directly reparented using {@link SurfaceControl.Transaction} this
@@ -273,6 +285,6 @@ public class SurfaceControlViewHost {
*/
public void release() {
// ViewRoot will release mSurfaceControl for us.
- mViewRoot.die(false /* immediate */);
+ mViewRoot.die(true /* immediate */);
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index bd811fc1f052823d0924f29b2cfa698733216ec0..f937bc9e84a9d5c1489d387c27d6c3041c680871 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -44,7 +44,6 @@ import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceControl.Transaction;
-import android.view.SurfaceControlViewHost;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
@@ -134,6 +133,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
// we need to preserve the old one until the new one has drawn.
SurfaceControl mDeferredDestroySurfaceControl;
SurfaceControl mBackgroundControl;
+ private boolean mDisableBackgroundLayer = false;
/**
* We use this lock in SOME cases when reading or writing SurfaceControl,
@@ -245,10 +245,17 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ this(context, attrs, defStyleAttr, defStyleRes, false);
+ }
+
+ /** @hide */
+ public SurfaceView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes, boolean disableBackgroundLayer) {
super(context, attrs, defStyleAttr, defStyleRes);
mRenderNode.addPositionUpdateListener(mPositionListener);
setWillNotDraw(true);
+ mDisableBackgroundLayer = disableBackgroundLayer;
}
/**
@@ -464,6 +471,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
private void performDrawFinished() {
+ if (mDeferredDestroySurfaceControl != null) {
+ synchronized (mSurfaceControlLock) {
+ mTmpTransaction.remove(mDeferredDestroySurfaceControl).apply();
+ mDeferredDestroySurfaceControl = null;
+ }
+ }
+
if (mPendingReportDraws > 0) {
mDrawFinished = true;
if (mAttachedToWindow) {
@@ -632,7 +646,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mTmpRect.set(0, 0, mSurfaceWidth, mSurfaceHeight);
}
SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(this);
- applier.scheduleApply(false /* earlyWakeup */,
+ applier.scheduleApply(
new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(mSurfaceControl)
.withWindowCrop(mTmpRect)
.build());
@@ -839,7 +853,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (mBackgroundControl == null) {
return;
}
- if ((mSubLayer < 0) && ((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)) {
+ if ((mSubLayer < 0) && ((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
+ && !mDisableBackgroundLayer) {
t.show(mBackgroundControl);
} else {
t.hide(mBackgroundControl);
@@ -884,12 +899,15 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
return;
}
- ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + " updateSurface: no valid surface");
- }
+ final ViewRootImpl viewRoot = getViewRootImpl();
+
+ if (viewRoot == null) {
+ return;
+ }
+
+ if (viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
+ notifySurfaceDestroyed();
+ releaseSurfaces();
return;
}
@@ -976,17 +994,21 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName(name)
+ .setLocalOwnerView(this)
.setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
.setBufferSize(mSurfaceWidth, mSurfaceHeight)
.setFormat(mFormat)
.setParent(viewRoot.getBoundsLayer())
.setFlags(mSurfaceFlags)
+ .setCallsite("SurfaceView.updateSurface")
.build();
mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
.setName("Background for -" + name)
+ .setLocalOwnerView(this)
.setOpaque(true)
.setColorLayer()
.setParent(mSurfaceControl)
+ .setCallsite("SurfaceView.updateSurface")
.build();
} else if (mSurfaceControl == null) {
@@ -1039,11 +1061,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
// we still need to latch a buffer).
// b/28866173
if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
- mTmpTransaction.setPosition(mSurfaceControl, mScreenRect.left,
- mScreenRect.top);
- mTmpTransaction.setMatrix(mSurfaceControl,
- mScreenRect.width() / (float) mSurfaceWidth, 0.0f, 0.0f,
- mScreenRect.height() / (float) mSurfaceHeight);
+ onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
+ mScreenRect.left, /*positionLeft*/
+ mScreenRect.top /*positionTop*/ ,
+ mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
+ mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
+
// Set a window crop when creating the surface or changing its size to
// crop the buffer to the surface size since the buffer producer may
// use SCALING_MODE_SCALE and submit a larger size than the surface
@@ -1100,28 +1123,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
final boolean surfaceChanged = creating;
if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
mSurfaceCreated = false;
- if (mSurface.isValid()) {
- if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
- + "visibleChanged -- surfaceDestroyed");
- callbacks = getSurfaceCallbacks();
- for (SurfaceHolder.Callback c : callbacks) {
- c.surfaceDestroyed(mSurfaceHolder);
- }
- // Since Android N the same surface may be reused and given to us
- // again by the system server at a later point. However
- // as we didn't do this in previous releases, clients weren't
- // necessarily required to clean up properly in
- // surfaceDestroyed. This leads to problems for example when
- // clients don't destroy their EGL context, and try
- // and create a new one on the same surface following reuse.
- // Since there is no valid use of the surface in-between
- // surfaceDestroyed and surfaceCreated, we force a disconnect,
- // so the next connect will always work if we end up reusing
- // the surface.
- if (mSurface.isValid()) {
- mSurface.forceScopedDisconnect();
- }
- }
+ notifySurfaceDestroyed();
}
if (creating) {
@@ -1199,11 +1201,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
+ "finishedDrawing");
}
- if (mDeferredDestroySurfaceControl != null) {
- mTmpTransaction.remove(mDeferredDestroySurfaceControl).apply();
- mDeferredDestroySurfaceControl = null;
- }
-
runOnUiThread(this::performDrawFinished);
}
@@ -1218,6 +1215,40 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
Surface viewRootSurface, long nextViewRootFrameNumber) {
}
+ /**
+ * Sets the surface position and scale. Can be called on
+ * the UI thread as well as on the renderer thread.
+ *
+ * @param transaction Transaction in which to execute.
+ * @param surface Surface whose location to set.
+ * @param positionLeft The left position to set.
+ * @param positionTop The top position to set.
+ * @param postScaleX The X axis post scale
+ * @param postScaleY The Y axis post scale
+ *
+ * @hide
+ */
+ protected void onSetSurfacePositionAndScaleRT(@NonNull Transaction transaction,
+ @NonNull SurfaceControl surface, int positionLeft, int positionTop,
+ float postScaleX, float postScaleY) {
+ transaction.setPosition(surface, positionLeft, positionTop);
+ transaction.setMatrix(surface, postScaleX /*dsdx*/, 0f /*dtdx*/,
+ 0f /*dtdy*/, postScaleY /*dsdy*/);
+ }
+
+ /** @hide */
+ public void requestUpdateSurfacePositionAndScale() {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
+ mScreenRect.left, /*positionLeft*/
+ mScreenRect.top/*positionTop*/ ,
+ mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
+ mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
+ mTmpTransaction.apply();
+ }
+
private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
Rect position, long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
@@ -1226,16 +1257,26 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
frameNumber);
}
- t.setPosition(surface, position.left, position.top);
- t.setMatrix(surface,
- position.width() / (float) mSurfaceWidth,
- 0.0f, 0.0f,
- position.height() / (float) mSurfaceHeight);
+ onSetSurfacePositionAndScaleRT(t, surface,
+ position.left /*positionLeft*/,
+ position.top /*positionTop*/,
+ position.width() / (float) mSurfaceWidth /*postScaleX*/,
+ position.height() / (float) mSurfaceHeight /*postScaleY*/);
+
if (mViewVisibility) {
t.show(surface);
}
}
+ /**
+ * @return The last render position of the backing surface or an empty rect.
+ *
+ * @hide
+ */
+ public @NonNull Rect getSurfaceRenderPosition() {
+ return mRTLastReportedPosition;
+ }
+
private void setParentSpaceRectangle(Rect position, long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
final boolean useBLAST = viewRoot.isDrawingToBLASTTransaction();
@@ -1627,9 +1668,14 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
private void updateRelativeZ(Transaction t) {
- SurfaceControl viewRoot = getViewRootImpl().getSurfaceControl();
- t.setRelativeLayer(mBackgroundControl, viewRoot, Integer.MIN_VALUE);
- t.setRelativeLayer(mSurfaceControl, viewRoot, mSubLayer);
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot == null) {
+ // We were just detached.
+ return;
+ }
+ final SurfaceControl viewRootControl = viewRoot.getSurfaceControl();
+ t.setRelativeLayer(mBackgroundControl, viewRootControl, Integer.MIN_VALUE);
+ t.setRelativeLayer(mSurfaceControl, viewRootControl, mSubLayer);
}
/**
@@ -1775,6 +1821,31 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
+ private void notifySurfaceDestroyed() {
+ if (mSurface.isValid()) {
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "surfaceDestroyed");
+ SurfaceHolder.Callback[] callbacks = getSurfaceCallbacks();
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceDestroyed(mSurfaceHolder);
+ }
+ // Since Android N the same surface may be reused and given to us
+ // again by the system server at a later point. However
+ // as we didn't do this in previous releases, clients weren't
+ // necessarily required to clean up properly in
+ // surfaceDestroyed. This leads to problems for example when
+ // clients don't destroy their EGL context, and try
+ // and create a new one on the same surface following reuse.
+ // Since there is no valid use of the surface in-between
+ // surfaceDestroyed and surfaceCreated, we force a disconnect,
+ // so the next connect will always work if we end up reusing
+ // the surface.
+ if (mSurface.isValid()) {
+ mSurface.forceScopedDisconnect();
+ }
+ }
+ }
+
/**
* Wrapper of accessibility embedded connection for embedded view hierarchy.
*/
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index 9c97f3e5b503c45a9bd9cf9704a1e41d4a89536f..062285ff2f5dc57cbd223efe031d12b96ea43add 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -53,11 +53,10 @@ public class SyncRtSurfaceTransactionApplier {
/**
* Schedules applying surface parameters on the next frame.
*
- * @param earlyWakeup Whether to set {@link Transaction#setEarlyWakeup()} on transaction.
* @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
* this method to avoid synchronization issues.
*/
- public void scheduleApply(boolean earlyWakeup, final SurfaceParams... params) {
+ public void scheduleApply(final SurfaceParams... params) {
if (mTargetViewRootImpl == null) {
return;
}
@@ -67,7 +66,7 @@ public class SyncRtSurfaceTransactionApplier {
return;
}
Transaction t = new Transaction();
- applyParams(t, frame, earlyWakeup, params);
+ applyParams(t, frame, params);
});
// Make sure a frame gets scheduled.
@@ -78,12 +77,10 @@ public class SyncRtSurfaceTransactionApplier {
* Applies surface parameters on the next frame.
* @param t transaction to apply all parameters in.
* @param frame frame to synchronize to. Set -1 when sync is not required.
- * @param earlyWakeup Whether to set {@link Transaction#setEarlyWakeup()} on transaction.
* @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
* this method to avoid synchronization issues.
*/
- void applyParams(Transaction t, long frame, boolean earlyWakeup,
- final SurfaceParams... params) {
+ void applyParams(Transaction t, long frame, final SurfaceParams... params) {
for (int i = params.length - 1; i >= 0; i--) {
SurfaceParams surfaceParams = params[i];
SurfaceControl surface = surfaceParams.surface;
@@ -92,9 +89,6 @@ public class SyncRtSurfaceTransactionApplier {
}
applyParams(t, surfaceParams, mTmpFloat9);
}
- if (earlyWakeup) {
- t.setEarlyWakeup();
- }
t.apply();
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1226202dfdf9a17181e6ee86d86821e8e4a9f15b..ca424e79ed7b7c74f2c8fac13a174dd6b89edb33 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14274,6 +14274,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
+ if (event.isTargetAccessibilityFocus()) {
+ // We don't have focus or no virtual descendant has it, do not handle the event.
+ if (!isAccessibilityFocusedViewOrHost()) {
+ return false;
+ }
+ // We have focus and got the event, then use normal event dispatch.
+ event.setTargetAccessibilityFocus(false);
+ }
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
@@ -26364,6 +26372,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
.setParent(root.getSurfaceControl())
.setBufferSize(shadowSize.x, shadowSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
+ .setCallsite("View.startDragAndDrop")
.build();
final Surface surface = new Surface();
surface.copyFrom(surfaceControl);
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 0d2d4d13eb387a10dc2bd60e7425f08a58243393..ffeeb806ba54fa5a7204ce30cfcc6f2f1b478eaf 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -500,12 +500,13 @@ public class ViewConfiguration {
*/
public static ViewConfiguration get(Context context) {
if (!context.isUiContext() && vmIncorrectContextUseEnabled()) {
- final String errorMessage = "Tried to access UI constants from a non-visual Context.";
+ final String errorMessage = "Tried to access UI constants from a non-visual Context:"
+ + context;
final String message = "UI constants, such as display metrics or window metrics, "
+ "must be accessed from Activity or other visual Context. "
+ "Use an Activity or a Context created with "
+ "Context#createWindowContext(int, Bundle), which are adjusted to the "
- + "configuration and visual bounds of an area on screen.";
+ + "configuration and visual bounds of an area on screen";
final Exception exception = new IllegalArgumentException(errorMessage);
StrictMode.onIncorrectContextUsed(message, exception);
Log.e(TAG, errorMessage + message, exception);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index e3362aafbcd465ea3551b69f39d95d3dab3fbe53..77fedd7c30d4ac173994774cdb15de22140e66f8 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2048,8 +2048,26 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
+ View childWithAccessibilityFocus =
+ event.isTargetAccessibilityFocus()
+ ? findChildWithAccessibilityFocus()
+ : null;
+
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
+
+ // If there is a view that has accessibility focus we want it
+ // to get the event first and if not handled we will perform a
+ // normal dispatch. We may do a double iteration but this is
+ // safer given the timeframe.
+ if (childWithAccessibilityFocus != null) {
+ if (childWithAccessibilityFocus != child) {
+ continue;
+ }
+ childWithAccessibilityFocus = null;
+ i = childrenCount - 1;
+ }
+ event.setTargetAccessibilityFocus(false);
continue;
}
final PointerIcon pointerIcon =
@@ -2617,6 +2635,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
+ // If the event targets the accessibility focused view and this is it, start
+ // normal event dispatch. Maybe a descendant is what will handle the click.
+ if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
+ ev.setTargetAccessibilityFocus(false);
+ }
+
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
@@ -2647,6 +2671,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// so this view group continues to intercept touches.
intercepted = true;
}
+
+ // If intercepted, start normal event dispatch. Also if there is already
+ // a view that is handling the gesture, do normal event dispatch.
+ if (intercepted || mFirstTouchTarget != null) {
+ ev.setTargetAccessibilityFocus(false);
+ }
+
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
@@ -2658,6 +2689,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
+ // If the event is targeting accessibility focus we give it to the
+ // view that has accessibility focus and if it does not handle it
+ // we clear the flag and dispatch the event to all children as usual.
+ // We are looking up the accessibility focused host to avoid keeping
+ // state since these events are very rare.
+ View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
+ ? findChildWithAccessibilityFocus() : null;
+
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
@@ -2720,6 +2759,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
alreadyDispatchedToNewTouchTarget = true;
break;
}
+
+ // The accessibility focus didn't handle the event, so clear
+ // the flag and do a normal dispatch to all children.
+ ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
@@ -2803,6 +2846,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return buildOrderedChildList();
}
+ /**
+ * Finds the child which has accessibility focus.
+ *
+ * @return The child that has focus.
+ */
+ private View findChildWithAccessibilityFocus() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot == null) {
+ return null;
+ }
+
+ View current = viewRoot.getAccessibilityFocusedHost();
+ if (current == null) {
+ return null;
+ }
+
+ ViewParent parent = current.getParent();
+ while (parent instanceof View) {
+ if (parent == this) {
+ return current;
+ }
+ current = (View) parent;
+ parent = current.getParent();
+ }
+
+ return null;
+ }
+
/**
* Resets all touch state in preparation for a new cycle.
*/
@@ -3257,9 +3328,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
break;
}
default:
- throw new IllegalStateException("descendant focusability must be "
- + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
- + "but is " + descendantFocusability);
+ throw new IllegalStateException(
+ "descendant focusability must be one of FOCUS_BEFORE_DESCENDANTS,"
+ + " FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS but is "
+ + descendantFocusability);
}
if (result && !isLayoutValid() && ((mPrivateFlags & PFLAG_WANTS_FOCUS) == 0)) {
mPrivateFlags |= PFLAG_WANTS_FOCUS;
@@ -4925,7 +4997,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if (params == null) {
params = generateDefaultLayoutParams();
if (params == null) {
- throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
+ throw new IllegalArgumentException(
+ "generateDefaultLayoutParams() cannot return null");
}
}
addView(child, index, params);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8b5d033072fbbd3d222836605846b4528c6ce47e..fefe564787ca41b78899e517a42dde7dcbc26c33 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -21,7 +21,7 @@ import static android.view.Display.INVALID_DISPLAY;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.LAST_TYPE;
+import static android.view.InsetsState.SIZE;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -564,7 +564,7 @@ public final class ViewRootImpl implements ViewParent,
new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
boolean mPendingAlwaysConsumeSystemBars;
private final InsetsState mTempInsets = new InsetsState();
- private final InsetsSourceControl[] mTempControls = new InsetsSourceControl[LAST_TYPE + 1];
+ private final InsetsSourceControl[] mTempControls = new InsetsSourceControl[SIZE];
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
@@ -1726,8 +1726,10 @@ public final class ViewRootImpl implements ViewParent,
destroySurface();
}
}
+ scheduleConsumeBatchedInputImmediately();
}
+
/** Register callbacks to be notified when the ViewRootImpl surface changes. */
interface SurfaceChangedCallback {
void surfaceCreated(Transaction t);
@@ -1780,6 +1782,7 @@ public final class ViewRootImpl implements ViewParent,
.setContainerLayer()
.setName("Bounds for - " + getTitle().toString())
.setParent(getRenderSurfaceControl())
+ .setCallsite("ViewRootImpl.getBoundsLayer")
.build();
setBoundsLayerCrop();
mTransaction.show(mBoundsLayer).apply();
@@ -1821,13 +1824,19 @@ public final class ViewRootImpl implements ViewParent,
/**
* Called after window layout to update the bounds surface. If the surface insets have changed
* or the surface has resized, update the bounds surface.
+ *
+ * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl.
*/
- private void updateBoundsLayer() {
+ private void updateBoundsLayer(boolean shouldReparent) {
if (mBoundsLayer != null) {
setBoundsLayerCrop();
- mTransaction.deferTransactionUntil(mBoundsLayer,
- getRenderSurfaceControl(), mSurface.getNextFrameNumber())
- .apply();
+ mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(),
+ mSurface.getNextFrameNumber());
+
+ if (shouldReparent) {
+ mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl());
+ }
+ mTransaction.apply();
}
}
@@ -2314,7 +2323,7 @@ public final class ViewRootImpl implements ViewParent,
|| lp.type == TYPE_VOLUME_OVERLAY;
}
- private int dipToPx(int dip) {
+ int dipToPx(int dip) {
final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
return (int) (displayMetrics.density * dip + 0.5f);
}
@@ -2722,7 +2731,6 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mThreadedRenderer.isEnabled()) {
mAttachInfo.mThreadedRenderer.destroy();
}
- notifySurfaceDestroyed();
} else if ((surfaceReplaced
|| surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged)
&& mSurfaceHolder == null
@@ -2910,7 +2918,16 @@ public final class ViewRootImpl implements ViewParent,
}
if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
- updateBoundsLayer();
+ // If the surface has been replaced, there's a chance the bounds layer is not parented
+ // to the new layer. When updating bounds layer, also reparent to the main VRI
+ // SurfaceControl to ensure it's correctly placed in the hierarchy.
+ //
+ // This needs to be done on the client side since WMS won't reparent the children to the
+ // new surface if it thinks the app is closing. WMS gets the signal that the app is
+ // stopping, but on the client side it doesn't get stopped since it's restarted quick
+ // enough. WMS doesn't want to keep around old children since they will leak when the
+ // client creates new children.
+ updateBoundsLayer(surfaceReplaced);
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
@@ -2953,6 +2970,10 @@ public final class ViewRootImpl implements ViewParent,
}
}
+ if (surfaceDestroyed) {
+ notifySurfaceDestroyed();
+ }
+
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
@@ -4616,6 +4637,9 @@ public final class ViewRootImpl implements ViewParent,
}
void dispatchDetachedFromWindow() {
+ // Make sure we free-up insets resources if view never received onWindowFocusLost()
+ // because of a die-signal
+ mInsetsController.onWindowFocusLost();
mFirstInputStage.onDetachedFromWindow();
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
@@ -8104,7 +8128,9 @@ public final class ViewRootImpl implements ViewParent,
}
void scheduleConsumeBatchedInput() {
- if (!mConsumeBatchedInputScheduled) {
+ // If anything is currently scheduled to consume batched input then there's no point in
+ // scheduling it again.
+ if (!mConsumeBatchedInputScheduled && !mConsumeBatchedInputImmediatelyScheduled) {
mConsumeBatchedInputScheduled = true;
mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
mConsumedBatchedInputRunnable, null);
@@ -8127,22 +8153,15 @@ public final class ViewRootImpl implements ViewParent,
}
}
- void doConsumeBatchedInput(long frameTimeNanos) {
- if (mConsumeBatchedInputScheduled) {
- mConsumeBatchedInputScheduled = false;
- if (mInputEventReceiver != null) {
- if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)
- && frameTimeNanos != -1) {
- // If we consumed a batch here, we want to go ahead and schedule the
- // consumption of batched input events on the next frame. Otherwise, we would
- // wait until we have more input events pending and might get starved by other
- // things occurring in the process. If the frame time is -1, however, then
- // we're in a non-batching mode, so there's no need to schedule this.
- scheduleConsumeBatchedInput();
- }
- }
- doProcessInputEvents();
+ boolean doConsumeBatchedInput(long frameTimeNanos) {
+ final boolean consumedBatches;
+ if (mInputEventReceiver != null) {
+ consumedBatches = mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos);
+ } else {
+ consumedBatches = false;
}
+ doProcessInputEvents();
+ return consumedBatches;
}
final class TraversalRunnable implements Runnable {
@@ -8186,8 +8205,11 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void onBatchedInputEventPending(int source) {
+ // mStopped: There will be no more choreographer callbacks if we are stopped,
+ // so we must consume all input immediately to prevent ANR
final boolean unbuffered = mUnbufferedInputDispatch
- || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE;
+ || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE
+ || mStopped;
if (unbuffered) {
if (mConsumeBatchedInputScheduled) {
unscheduleConsumeBatchedInput();
@@ -8215,7 +8237,14 @@ public final class ViewRootImpl implements ViewParent,
final class ConsumeBatchedInputRunnable implements Runnable {
@Override
public void run() {
- doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
+ mConsumeBatchedInputScheduled = false;
+ if (doConsumeBatchedInput(mChoreographer.getFrameTimeNanos())) {
+ // If we consumed a batch here, we want to go ahead and schedule the
+ // consumption of batched input events on the next frame. Otherwise, we would
+ // wait until we have more input events pending and might get starved by other
+ // things occurring in the process.
+ scheduleConsumeBatchedInput();
+ }
}
}
final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable =
@@ -8225,6 +8254,7 @@ public final class ViewRootImpl implements ViewParent,
final class ConsumeBatchedInputImmediatelyRunnable implements Runnable {
@Override
public void run() {
+ mConsumeBatchedInputImmediatelyScheduled = false;
doConsumeBatchedInput(-1);
}
}
@@ -9365,6 +9395,11 @@ public final class ViewRootImpl implements ViewParent,
return mInputEventReceiver.getToken();
}
+ @NonNull
+ public IBinder getWindowToken() {
+ return mAttachInfo.mWindowToken;
+ }
+
/**
* Class for managing the accessibility interaction connection
* based on the global accessibility state.
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index 686d561a1a5a51bccb27f3a90bb124262eb61ad9..90a80cefc54d770de4458c54194ab019626e38fe 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONT
import android.annotation.NonNull;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
@@ -120,13 +121,12 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host {
mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView);
}
if (mViewRoot.mView.isHardwareAccelerated()) {
- mApplier.scheduleApply(false /* earlyWakeup */, params);
+ mApplier.scheduleApply(params);
} else {
// Window doesn't support hardware acceleration, no synchronization for now.
// TODO(b/149342281): use mViewRoot.mSurface.getNextFrameNumber() to sync on every
// frame instead.
- mApplier.applyParams(new SurfaceControl.Transaction(), -1 /* frame */,
- false /* earlyWakeup */, params);
+ mApplier.applyParams(new SurfaceControl.Transaction(), -1 /* frame */, params);
}
}
@@ -229,4 +229,24 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host {
}
return mViewRoot.getTitle().toString();
}
+
+ @Override
+ public int dipToPx(int dips) {
+ if (mViewRoot != null) {
+ return mViewRoot.dipToPx(dips);
+ }
+ return 0;
+ }
+
+ @Override
+ public IBinder getWindowToken() {
+ if (mViewRoot == null) {
+ return null;
+ }
+ final View view = mViewRoot.getView();
+ if (view == null) {
+ return null;
+ }
+ return view.getWindowToken();
+ }
}
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index fb9d05e2d730c68b58ca3d151575f920b64720a6..792b974558bb9312bf7291d4e161d11c9c150117 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -181,4 +181,11 @@ public interface WindowInsetsAnimationController {
* @return {@code true} if the instance is cancelled, {@code false} otherwise.
*/
boolean isCancelled();
+
+ /**
+ * @hide
+ * @return {@code true} when controller controls IME and IME has no insets (floating,
+ * fullscreen or non-overlapping).
+ */
+ boolean hasZeroInsetsIme();
}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 3d348efc7f0ff745116dc15813eadc88baf8bfa6..1a9003581078924c1a57646b80618ea200d70103 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -221,6 +221,13 @@ public interface WindowInsetsController {
*/
@Behavior int getSystemBarsBehavior();
+ /**
+ * Disables or enables the animations.
+ *
+ * @hide
+ */
+ void setAnimationsDisabled(boolean disable);
+
/**
* @hide
*/
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 76071278edf832fe3e3cce4799951137c4be8f34..5c6269421a1f6a1b93ea0fe1cf583a4e11d8e8ef 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1454,11 +1454,22 @@ public interface WindowManager extends ViewManager {
@Deprecated
public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;
- /** Window flag: When set, input method can't interact with the focusable window
- * and can be placed to use more space and cover the input method.
- * Note: When combined with {@link #FLAG_NOT_FOCUSABLE}, this flag has no
- * effect since input method cannot interact with windows having {@link #FLAG_NOT_FOCUSABLE}
- * flag set.
+ /** Window flag: when set, inverts the input method focusability of the window.
+ *
+ * The effect of setting this flag depends on whether {@link #FLAG_NOT_FOCUSABLE} is set:
+ *
+ * If {@link #FLAG_NOT_FOCUSABLE} is not set, i.e. when the window is focusable,
+ * setting this flag prevents this window from becoming the target of the input method.
+ * Consequently, it will not be able to interact with the input method,
+ * and will be layered above the input method (unless there is another input method
+ * target above it).
+ *
+ *
+ * If {@link #FLAG_NOT_FOCUSABLE} is set, setting this flag requests for the window
+ * to be the input method target even though the window is not focusable.
+ * Consequently, it will be layered below the input method.
+ * Note: Windows that set {@link #FLAG_NOT_FOCUSABLE} cannot interact with the input method,
+ * regardless of this flag.
*/
public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;
@@ -2142,13 +2153,12 @@ public interface WindowManager extends ViewManager {
* focus. In particular, this checks the
* {@link #FLAG_NOT_FOCUSABLE} and {@link #FLAG_ALT_FOCUSABLE_IM}
* flags and returns true if the combination of the two corresponds
- * to a window that needs to be behind the input method so that the
- * user can type into it.
+ * to a window that can use the input method.
*
* @param flags The current window manager flags.
*
- * @return Returns {@code true} if such a window should be behind/interact
- * with an input method, {@code false} if not.
+ * @return Returns {@code true} if a window with the given flags would be able to
+ * use the input method, {@code false} if not.
*/
public static boolean mayUseInputMethod(int flags) {
return (flags & FLAG_NOT_FOCUSABLE) != FLAG_NOT_FOCUSABLE
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index a5ec4e90064145079e39068e73628c127d3b1c43..1af4c3636ac55e640c1242fdf30259cc43316cab 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -41,7 +41,6 @@ public class WindowlessWindowManager implements IWindowSession {
private final static String TAG = "WindowlessWindowManager";
private class State {
- //TODO : b/150190730 we should create it when view show and release it when view invisible.
SurfaceControl mSurfaceControl;
WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
int mDisplayId;
@@ -137,7 +136,8 @@ public class WindowlessWindowManager implements IWindowSession {
.setParent(mRootSurface)
.setFormat(attrs.format)
.setBufferSize(getSurfaceWidth(attrs), getSurfaceHeight(attrs))
- .setName(attrs.getTitle().toString());
+ .setName(attrs.getTitle().toString())
+ .setCallsite("WindowlessWindowManager.addToDisplay");
final SurfaceControl sc = b.build();
if (((attrs.inputFeatures &
@@ -249,7 +249,7 @@ public class WindowlessWindowManager implements IWindowSession {
if (viewFlags == View.VISIBLE) {
t.setBufferSize(sc, getSurfaceWidth(attrs), getSurfaceHeight(attrs))
.setOpaque(sc, isOpaque(attrs)).show(sc).apply();
- outSurfaceControl.copyFrom(sc);
+ outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout");
} else {
t.hide(sc).apply();
outSurfaceControl.release();
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index eaaaa80f65ed83ba688aa3ff6e2ea98dd71c2a48..214da380ccdad1bcf0309544dd1ebe85ac985db1 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -635,7 +635,7 @@ public class AccessibilityNodeInfo implements Parcelable {
"android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
/**
- * Key used to request extra data for accessibility scanning tool's purposes.
+ * Key used to request extra data for the rendering information.
* The key requests that a {@link AccessibilityNodeInfo.ExtraRenderingInfo} be added to this
* info. This request is made with {@link #refreshWithExtraData(String, Bundle)} without
* argument.
@@ -5847,12 +5847,15 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
- * Gets the size object containing the height and the width of layout params if the node is
- * a {@link ViewGroup} or a {@link TextView}, or null otherwise. Useful for accessibility
- * scanning tool to understand whether the text is scalable and fits the view or not.
+ * Gets the size object containing the height and the width of
+ * {@link android.view.ViewGroup.LayoutParams} if the node is a {@link ViewGroup} or
+ * a {@link TextView}, or null otherwise. Useful for some accessibility services to
+ * understand whether the text is scalable and fits the view or not.
*
- * @return a {@link Size} stores layout height and layout width of the view,
- * or null otherwise.
+ * @return a {@link Size} stores layout height and layout width of the view, or null
+ * otherwise. And the size value may be in pixels,
+ * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
+ * or {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
*/
public @Nullable Size getLayoutSize() {
return mLayoutSize;
@@ -5870,9 +5873,9 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
- * Gets the text size if the node is a {@link TextView}, or -1 otherwise. Useful for
- * accessibility scanning tool to understand whether the text is scalable and fits the view
- * or not.
+ * Gets the text size if the node is a {@link TextView}, or -1 otherwise. Useful for some
+ * accessibility services to understand whether the text is scalable and fits the view or
+ * not.
*
* @return the text size of a {@code TextView}, or -1 otherwise.
*/
@@ -5893,7 +5896,7 @@ public class AccessibilityNodeInfo implements Parcelable {
/**
* Gets the text size unit if the node is a {@link TextView}, or -1 otherwise.
* Text size returned from {@link #getTextSizeInPx} in raw pixels may scale by factors and
- * convert from other units. Useful for accessibility scanning tool to understand whether
+ * convert from other units. Useful for some accessibility services to understand whether
* the text is scalable and fits the view or not.
*
* @return the text size unit which type is {@link TypedValue#TYPE_DIMENSION} of a
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 9cdaba599d94ac5976f56d61c809c593eedff8e3..32b9cf7cdbb017b7f5e87d07d272d66d1a631e26 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -77,7 +77,10 @@ public final class AutofillId implements Parcelable {
@TestApi
public static AutofillId withoutSession(@NonNull AutofillId id) {
final int flags = id.mFlags & ~FLAG_HAS_SESSION;
- return new AutofillId(flags, id.mViewId, id.mVirtualLongId, NO_SESSION);
+ final long virtualChildId =
+ ((id.mFlags & FLAG_IS_VIRTUAL_LONG) != 0) ? id.mVirtualLongId
+ : id.mVirtualIntId;
+ return new AutofillId(flags, id.mViewId, virtualChildId, NO_SESSION);
}
/** @hide */
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 3112039c36d8fe199f95c8e84426247a2a1ccc1a..fbfeda6f0bccf8ab71f83e71025dd8ee8dbdf08c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -751,6 +751,8 @@ public final class AutofillManager {
}
} catch (RemoteException e) {
Log.e(TAG, "Could not figure out if there was an autofill session", e);
+ } catch (SyncResultReceiver.TimeoutException e) {
+ Log.e(TAG, "Fail to get session restore status: " + e);
}
}
}
@@ -864,7 +866,9 @@ public final class AutofillManager {
mService.getFillEventHistory(receiver);
return receiver.getParcelableResult();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ Log.e(TAG, "Fail to get fill event history: " + e);
return null;
}
}
@@ -1477,10 +1481,13 @@ public final class AutofillManager {
final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
- mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver);
+ mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(),
+ receiver);
return receiver.getIntResult() == 1;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get enabled autofill services status.");
}
}
@@ -1498,6 +1505,8 @@ public final class AutofillManager {
return receiver.getParcelableResult();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get autofill services component name.");
}
}
@@ -1522,8 +1531,9 @@ public final class AutofillManager {
mService.getUserDataId(receiver);
return receiver.getStringResult();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
+ throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get user data id for field classification.");
}
}
@@ -1544,8 +1554,9 @@ public final class AutofillManager {
mService.getUserData(receiver);
return receiver.getParcelableResult();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
+ throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get user data for field classification.");
}
}
@@ -1561,7 +1572,7 @@ public final class AutofillManager {
try {
mService.setUserData(userData);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -1583,8 +1594,9 @@ public final class AutofillManager {
mService.isFieldClassificationEnabled(receiver);
return receiver.getIntResult() == 1;
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return false;
+ throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get field classification enabled status.");
}
}
@@ -1606,8 +1618,9 @@ public final class AutofillManager {
mService.getDefaultFieldClassificationAlgorithm(receiver);
return receiver.getStringResult();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
+ throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get default field classification algorithm.");
}
}
@@ -1627,8 +1640,9 @@ public final class AutofillManager {
final String[] algorithms = receiver.getStringArrayResult();
return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
+ throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get available field classification algorithms.");
}
}
@@ -1651,6 +1665,8 @@ public final class AutofillManager {
return receiver.getIntResult() == 1;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get autofill supported status.");
}
}
@@ -2040,13 +2056,16 @@ public final class AutofillManager {
}
final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
- final int resultCode;
+ int resultCode;
try {
mService.setAugmentedAutofillWhitelist(toList(packages), toList(activities),
resultReceiver);
resultCode = resultReceiver.getIntResult();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ Log.e(TAG, "Fail to get the result of set AugmentedAutofill whitelist. " + e);
+ return;
}
switch (resultCode) {
case RESULT_OK:
@@ -2283,7 +2302,7 @@ public final class AutofillManager {
// In theory, we could ignore this error since it's not a big deal, but
// in reality, we rather crash the app anyways, as the failure could be
// a consequence of something going wrong on the server side...
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -2661,7 +2680,7 @@ public final class AutofillManager {
try {
mService.onPendingSaveUi(operation, token);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ Log.e(TAG, "Error in onPendingSaveUi: ", e);
}
}
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 756ff78e5906e8165f09beed40e2e9cb391f7424..484b1c10423cf2154461a6418853da3e49cd5c69 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -487,6 +487,8 @@ public final class ContentCaptureManager {
return resultReceiver.getParcelableResult();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get service componentName.");
}
}
@@ -516,6 +518,9 @@ public final class ContentCaptureManager {
return resultReceiver.getParcelableResult();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ Log.e(TAG, "Fail to get service settings componentName: " + e);
+ return null;
}
}
@@ -567,9 +572,13 @@ public final class ContentCaptureManager {
final SyncResultReceiver resultReceiver = syncRun(
(r) -> mService.getContentCaptureConditions(mContext.getPackageName(), r));
- final ArrayList result = resultReceiver
- .getParcelableListResult();
- return toSet(result);
+ try {
+ final ArrayList result = resultReceiver
+ .getParcelableListResult();
+ return toSet(result);
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get content capture conditions.");
+ }
}
/**
@@ -639,15 +648,21 @@ public final class ContentCaptureManager {
public boolean isContentCaptureFeatureEnabled() {
final SyncResultReceiver resultReceiver = syncRun(
(r) -> mService.isContentCaptureFeatureEnabled(r));
- final int resultCode = resultReceiver.getIntResult();
- switch (resultCode) {
- case RESULT_CODE_TRUE:
- return true;
- case RESULT_CODE_FALSE:
- return false;
- default:
- Log.wtf(TAG, "received invalid result: " + resultCode);
- return false;
+
+ try {
+ final int resultCode = resultReceiver.getIntResult();
+ switch (resultCode) {
+ case RESULT_CODE_TRUE:
+ return true;
+ case RESULT_CODE_FALSE:
+ return false;
+ default:
+ Log.wtf(TAG, "received invalid result: " + resultCode);
+ return false;
+ }
+ } catch (SyncResultReceiver.TimeoutException e) {
+ Log.e(TAG, "Fail to get content capture feature enable status: " + e);
+ return false;
}
}
@@ -663,7 +678,7 @@ public final class ContentCaptureManager {
try {
mService.removeData(request);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -691,7 +706,7 @@ public final class ContentCaptureManager {
new DataShareAdapterDelegate(executor, dataShareWriteAdapter,
mDataShareAdapterResourceManager));
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -709,10 +724,12 @@ public final class ContentCaptureManager {
if (resultCode == RESULT_CODE_SECURITY_EXCEPTION) {
throw new SecurityException(resultReceiver.getStringResult());
}
- return resultReceiver;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ throw new RuntimeException("Fail to get syn run result from SyncResultReceiver.");
}
+ return resultReceiver;
}
/** @hide */
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 301ce9f013e429959584bfc33f16f046f40cbd07..3f5ef5a2651d3071de3d71039716776a1a0741cb 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -39,8 +39,8 @@ import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.SecureRandom;
import java.util.ArrayList;
-import java.util.Random;
/**
* Session used when the Android a system-provided content capture service
@@ -50,7 +50,9 @@ public abstract class ContentCaptureSession implements AutoCloseable {
private static final String TAG = ContentCaptureSession.class.getSimpleName();
- private static final Random sIdGenerator = new Random();
+ // TODO(b/158778794): to make the session ids truly globally unique across
+ // processes, we may need to explore other options.
+ private static final SecureRandom ID_GENERATOR = new SecureRandom();
/**
* Initial state, when there is no session.
@@ -622,7 +624,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
private static int getRandomSessionId() {
int id;
do {
- id = sIdGenerator.nextInt();
+ id = ID_GENERATOR.nextInt();
} while (id == NO_SESSION_ID);
return id;
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 6eb71f747be6f28b66547798d3b41aefd2ed8e8c..c43beea98c7e21077299654b4133303b3bc14dd7 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -52,6 +52,7 @@ import android.view.contentcapture.ViewNode.ViewStructureImpl;
import com.android.internal.os.IResultReceiver;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -146,7 +147,44 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
* Binder object used to update the session state.
*/
@NonNull
- private final IResultReceiver.Stub mSessionStateReceiver;
+ private final SessionStateReceiver mSessionStateReceiver;
+
+ private static class SessionStateReceiver extends IResultReceiver.Stub {
+ private final WeakReference mMainSession;
+
+ SessionStateReceiver(MainContentCaptureSession session) {
+ mMainSession = new WeakReference<>(session);
+ }
+
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ final MainContentCaptureSession mainSession = mMainSession.get();
+ if (mainSession == null) {
+ Log.w(TAG, "received result after mina session released");
+ return;
+ }
+ final IBinder binder;
+ if (resultData != null) {
+ // Change in content capture enabled.
+ final boolean hasEnabled = resultData.getBoolean(EXTRA_ENABLED_STATE);
+ if (hasEnabled) {
+ final boolean disabled = (resultCode == RESULT_CODE_FALSE);
+ mainSession.mDisabled.set(disabled);
+ return;
+ }
+ binder = resultData.getBinder(EXTRA_BINDER);
+ if (binder == null) {
+ Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
+ mainSession.mHandler.post(() -> mainSession.resetSession(
+ STATE_DISABLED | STATE_INTERNAL_ERROR));
+ return;
+ }
+ } else {
+ binder = null;
+ }
+ mainSession.mHandler.post(() -> mainSession.onSessionStarted(resultCode, binder));
+ }
+ }
protected MainContentCaptureSession(@NonNull Context context,
@NonNull ContentCaptureManager manager, @NonNull Handler handler,
@@ -159,32 +197,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
final int logHistorySize = mManager.mOptions.logHistorySize;
mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
- mSessionStateReceiver = new IResultReceiver.Stub() {
- @Override
- public void send(int resultCode, Bundle resultData) {
- final IBinder binder;
- if (resultData != null) {
- // Change in content capture enabled.
- final boolean hasEnabled = resultData.getBoolean(EXTRA_ENABLED_STATE);
- if (hasEnabled) {
- final boolean disabled = (resultCode == RESULT_CODE_FALSE);
- mDisabled.set(disabled);
- return;
- }
- binder = resultData.getBinder(EXTRA_BINDER);
- if (binder == null) {
- Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
- mHandler.post(() -> resetSession(
- STATE_DISABLED | STATE_INTERNAL_ERROR));
- return;
- }
- } else {
- binder = null;
- }
- mHandler.post(() -> onSessionStarted(resultCode, binder));
- }
- };
-
+ mSessionStateReceiver = new SessionStateReceiver(this);
}
@Override
@@ -543,6 +556,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
Log.e(TAG, "Error destroying system-service session " + mId + " for "
+ getDebugState() + ": " + e);
}
+
+ if (mDirectServiceInterface != null) {
+ mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+ }
+ mDirectServiceInterface = null;
}
// TODO(b/122454205): once we support multiple sessions, we might need to move some of these
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index e4ac5889a3c0d7f5ad5d127e26c8377d1ce63869..b8893cee834d5a58c99b02ecd70811f803d325d4 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -317,8 +317,24 @@ public final class InlineSuggestion implements Parcelable {
*/
@MainThread
private void handleOnSurfacePackage(SurfaceControlViewHost.SurfacePackage surfacePackage) {
+ if (surfacePackage == null) {
+ return;
+ }
+ if (mSurfacePackage != null || mSurfacePackageConsumer == null) {
+ // The surface package is not consumed, release it immediately.
+ surfacePackage.release();
+ try {
+ mInlineContentProvider.onSurfacePackageReleased();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error calling onSurfacePackageReleased(): " + e);
+ }
+ return;
+ }
mSurfacePackage = surfacePackage;
- if (mSurfacePackage != null && mSurfacePackageConsumer != null) {
+ if (mSurfacePackage == null) {
+ return;
+ }
+ if (mSurfacePackageConsumer != null) {
mSurfacePackageConsumer.accept(mSurfacePackage);
mSurfacePackageConsumer = null;
}
@@ -334,6 +350,10 @@ public final class InlineSuggestion implements Parcelable {
}
mSurfacePackage = null;
}
+ // Clear the pending surface package consumer, if any. This can happen if the IME
+ // attaches the view to window and then quickly detaches it from the window, before
+ // the surface package requested upon attaching to window was returned.
+ mSurfacePackageConsumer = null;
}
@MainThread
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 71dd6653f6a65b99312b18e875479a0a10e493d9..37b352940ee42521d86ed7f43744a279437d7fae 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -19,6 +19,9 @@ package android.view.inputmethod;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR;
+import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR;
+
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -538,6 +541,19 @@ public final class InputMethodManager {
return servedView.hasWindowFocus() || isAutofillUIShowing(servedView);
}
+ /**
+ * Reports whether the IME is currently perceptible or not, according to the leash applied by
+ * {@link android.view.WindowInsetsController}.
+ * @hide
+ */
+ public void reportPerceptible(IBinder windowToken, boolean perceptible) {
+ try {
+ mService.reportPerceptible(windowToken, perceptible);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private final class DelegateImpl implements
ImeFocusController.InputMethodManagerDelegate {
/**
@@ -616,12 +632,23 @@ public final class InputMethodManager {
// For some reason we didn't do a startInput + windowFocusGain, so
// we'll just do a window focus gain and call it a day.
try {
- if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
+ View servedView = controller.getServedView();
+ boolean nextFocusSameEditor = servedView != null && servedView == focusedView
+ && isSameEditorAndAcceptingText(focusedView);
+ if (DEBUG) {
+ Log.v(TAG, "Reporting focus gain, without startInput"
+ + ", nextFocusIsServedView=" + nextFocusSameEditor);
+ }
+ final int startInputReason =
+ nextFocusSameEditor ? WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR
+ : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR;
mService.startInputOrWindowGainedFocus(
- StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
+ startInputReason, mClient,
focusedView.getWindowToken(), startInputFlags, softInputMode,
windowFlags,
- null, null, 0 /* missingMethodFlags */,
+ null,
+ null,
+ 0 /* missingMethodFlags */,
mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -645,11 +672,6 @@ public final class InputMethodManager {
@Override
public void setCurrentRootView(ViewRootImpl rootView) {
synchronized (mH) {
- if (mCurRootView != null) {
- // Reset the last served view and restart window focus state of the root view.
- mCurRootView.getImeFocusController().setServedView(null);
- mRestartOnNextWindowFocus = true;
- }
mCurRootView = rootView;
}
}
@@ -677,6 +699,37 @@ public final class InputMethodManager {
}
return result;
}
+
+ /**
+ * For {@link ImeFocusController} to check if the given focused view aligns with the same
+ * editor and the editor is active to accept the text input.
+ *
+ * TODO(b/160968797): Remove this method and move mCurrentTextBoxAttritube to
+ * ImeFocusController.
+ * In the long-term, we should make mCurrentTextBoxAtrtribue as per-window base instance,
+ * so that we we can directly check if the current focused view aligned with the same editor
+ * in the window without using this checking.
+ *
+ * Note that this method is only use for fixing start new input may ignored issue
+ * (e.g. b/160391516), DO NOT leverage this method to do another check.
+ */
+ public boolean isSameEditorAndAcceptingText(View view) {
+ synchronized (mH) {
+ if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null) {
+ return false;
+ }
+
+ final EditorInfo ic = mCurrentTextBoxAttribute;
+ // This sameEditor checking is based on using object hash comparison to check if
+ // some fields of the current EditorInfo (e.g. autoFillId, OpPackageName) the
+ // hash code is same as the given focused view.
+ final boolean sameEditor = view.onCheckIsTextEditor() && view.getId() == ic.fieldId
+ && view.getAutofillId() == ic.autofillId
+ && view.getContext().getOpPackageName() == ic.packageName;
+ return sameEditor && mServedInputConnectionWrapper != null
+ && mServedInputConnectionWrapper.isActive();
+ }
+ }
}
/** @hide */
@@ -2072,28 +2125,36 @@ public final class InputMethodManager {
/**
* Call showSoftInput with currently focused view.
- * @return {@code true} if IME can be shown.
+ *
+ * @param windowToken the window from which this request originates. If this doesn't match the
+ * currently served view, the request is ignored and returns {@code false}.
+ *
+ * @return {@code true} if IME can (eventually) be shown, {@code false} otherwise.
* @hide
*/
- public boolean requestImeShow(ResultReceiver resultReceiver) {
+ public boolean requestImeShow(IBinder windowToken) {
synchronized (mH) {
final View servedView = getServedViewLocked();
- if (servedView == null) {
+ if (servedView == null || servedView.getWindowToken() != windowToken) {
return false;
}
- showSoftInput(servedView, 0 /* flags */, resultReceiver);
+ showSoftInput(servedView, 0 /* flags */, null /* resultReceiver */);
return true;
}
}
/**
* Notify IME directly that it is no longer visible.
+ *
+ * @param windowToken the window from which this request originates. If this doesn't match the
+ * currently served view, the request is ignored.
* @hide
*/
- public void notifyImeHidden() {
+ public void notifyImeHidden(IBinder windowToken) {
synchronized (mH) {
try {
- if (mCurMethod != null) {
+ if (mCurMethod != null && mCurRootView != null
+ && mCurRootView.getWindowToken() == windowToken) {
mCurMethod.notifyImeHidden();
}
} catch (RemoteException re) {
@@ -2103,15 +2164,15 @@ public final class InputMethodManager {
/**
* Notify IME directly to remove surface as it is no longer visible.
+ * @param windowToken The client window token that requests the IME to remove its surface.
* @hide
*/
- public void removeImeSurface() {
+ public void removeImeSurface(IBinder windowToken) {
synchronized (mH) {
try {
- if (mCurMethod != null) {
- mCurMethod.removeImeSurface();
- }
- } catch (RemoteException re) {
+ mService.removeImeSurfaceFromWindow(windowToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
}
diff --git a/core/java/android/view/textclassifier/TEST_MAPPING b/core/java/android/view/textclassifier/TEST_MAPPING
index 01a6edecf21ecbe062ea483e988964e81738aae8..2f9e737dc213d6471e492fc03ac599d7ef3f08aa 100644
--- a/core/java/android/view/textclassifier/TEST_MAPPING
+++ b/core/java/android/view/textclassifier/TEST_MAPPING
@@ -10,6 +10,22 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsTextClassifierTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "TextClassifierServiceTest",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 7042f29fc4e407fb21d5d53209dd3d3f232d4d5c..4a655117619897f84349b557154ee11449097201 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -205,6 +205,8 @@ public class WebChromeClient {
*
Note that if the {@link WebChromeClient} is set to be {@code null},
* or if {@link WebChromeClient} is not set at all, the default dialog will
* be suppressed and Javascript execution will continue immediately.
+ *
Note that the default dialog does not inherit the {@link
+ * android.view.Display#FLAG_SECURE} flag from the parent window.
*
* @param view The WebView that initiated the callback.
* @param url The url of the page requesting the dialog.
@@ -240,6 +242,8 @@ public class WebChromeClient {
* or if {@link WebChromeClient} is not set at all, the default dialog will
* be suppressed and the default value of {@code false} will be returned to
* the JavaScript code immediately.
+ *
Note that the default dialog does not inherit the {@link
+ * android.view.Display#FLAG_SECURE} flag from the parent window.
*
* @param view The WebView that initiated the callback.
* @param url The url of the page requesting the dialog.
@@ -274,6 +278,8 @@ public class WebChromeClient {
* or if {@link WebChromeClient} is not set at all, the default dialog will
* be suppressed and {@code null} will be returned to the JavaScript code
* immediately.
+ *
Note that the default dialog does not inherit the {@link
+ * android.view.Display#FLAG_SECURE} flag from the parent window.
*
* @param view The WebView that initiated the callback.
* @param url The url of the page requesting the dialog.
@@ -308,6 +314,8 @@ public class WebChromeClient {
*
Note that if the {@link WebChromeClient} is set to be {@code null},
* or if {@link WebChromeClient} is not set at all, the default dialog will
* be suppressed and the navigation will be resumed immediately.
+ *
Note that the default dialog does not inherit the {@link
+ * android.view.Display#FLAG_SECURE} flag from the parent window.
*
* @param view The WebView that initiated the callback.
* @param url The url of the page requesting the dialog.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 51d37a53f21f6adaefc7d1bcd94741f22f946453..07a721f5a9c9134af9d8bc109cf52efd64bc3e19 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -6354,6 +6354,8 @@ public class Editor {
// The offsets of that last touch down event. Remembered to start selection there.
private int mMinTouchOffset, mMaxTouchOffset;
+ private boolean mGestureStayedInTapRegion;
+
// Where the user first starts the drag motion.
private int mStartOffset = -1;
@@ -6460,8 +6462,10 @@ public class Editor {
eventX, eventY);
// Double tap detection
- if (mTouchState.isMultiTapInSameArea() && (isMouse
- || mTouchState.isOnHandle() || isPositionOnText(eventX, eventY))) {
+ if (mGestureStayedInTapRegion
+ && mTouchState.isMultiTapInSameArea()
+ && (isMouse || isPositionOnText(eventX, eventY)
+ || mTouchState.isOnHandle())) {
if (TextView.DEBUG_CURSOR) {
logCursor("SelectionModifierCursorController: onTouchEvent",
"ACTION_DOWN: select and start drag");
@@ -6473,6 +6477,7 @@ public class Editor {
}
mDiscardNextActionUp = true;
}
+ mGestureStayedInTapRegion = true;
mHaventMovedEnoughToStartDrag = true;
}
break;
@@ -6488,6 +6493,14 @@ public class Editor {
break;
case MotionEvent.ACTION_MOVE:
+ if (mGestureStayedInTapRegion) {
+ final ViewConfiguration viewConfig = ViewConfiguration.get(
+ mTextView.getContext());
+ mGestureStayedInTapRegion = EditorTouchState.isDistanceWithin(
+ mTouchState.getLastDownX(), mTouchState.getLastDownY(),
+ eventX, eventY, viewConfig.getScaledDoubleTapTouchSlop());
+ }
+
if (mHaventMovedEnoughToStartDrag) {
mHaventMovedEnoughToStartDrag = !mTouchState.isMovedEnoughForDrag();
}
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index ff3ac0732aa210665fb558b580049204f61214b3..9eb63087a66eb3b8353b7a01f2ab50ba829edd44 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -174,12 +174,9 @@ public class EditorTouchState {
int touchSlop = config.getScaledTouchSlop();
mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
if (mMovedEnoughForDrag) {
- // If the direction of the swipe motion is within 30 degrees of vertical, it is
- // considered a vertical drag. We don't actually have to compute the angle to
- // implement the check though. When the angle is exactly 30 degrees from
- // vertical, 2*deltaX = distance. When the angle is less than 30 degrees from
- // vertical, 2*deltaX < distance.
- mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared;
+ // If the direction of the swipe motion is within 45 degrees of vertical, it is
+ // considered a vertical drag.
+ mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY);
}
}
} else if (action == MotionEvent.ACTION_CANCEL) {
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 8ea824d3ce82e74e00beeb1289badfe7d165202b..7fa8f9a315265cfa76ecabc89aabc5c019f27d23 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -1000,6 +1000,7 @@ public final class Magnifier {
.setName("magnifier surface")
.setFlags(SurfaceControl.HIDDEN)
.setParent(parentSurfaceControl)
+ .setCallsite("InternalPopupWindow")
.build();
mSurface = new Surface();
mSurface.copyFrom(mSurfaceControl);
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index e07181abe19c8044083b8f7007d719500cbbd37d..843700cef55ecdd1fe8739c9c14ddcef3a17eb30 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -971,7 +971,8 @@ public final class SelectionActionModeHelper {
return new TextClassifierEvent.LanguageDetectionEvent.Builder(eventType)
.setEventContext(classificationContext)
.setResultId(classification.getId())
- .setEntityTypes(language)
+ // b/158481016: Disable language logging.
+ //.setEntityTypes(language)
.setScores(score)
.setActionIndices(classification.getActions().indexOf(translateAction))
.setModelName(model)
diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java
index 2679c69be4f61c693d4b679da8d3cd386bfa9ffe..fb5d55dd21419a1633ead16d8a305224d8d84e02 100644
--- a/core/java/android/widget/ToastPresenter.java
+++ b/core/java/android/widget/ToastPresenter.java
@@ -27,7 +27,6 @@ import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -90,7 +89,7 @@ public class ToastPresenter {
// context to it. This is problematic for multi-user because callers can pass a context
// created via Context.createContextAsUser().
mAccessibilityManager = new AccessibilityManager(context, accessibilityManager,
- UserHandle.getCallingUserId());
+ context.getUserId());
mParams = createLayoutParams();
}
diff --git a/core/java/android/widget/inline/InlineContentView.java b/core/java/android/widget/inline/InlineContentView.java
index 8657e828a3f619a4d767ffdbcef1dd4457a89a70..9712311aab7c40c5292b64341157c337f8697200 100644
--- a/core/java/android/widget/inline/InlineContentView.java
+++ b/core/java/android/widget/inline/InlineContentView.java
@@ -18,16 +18,22 @@ package android.widget.inline;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.Context;
import android.graphics.PixelFormat;
+import android.graphics.PointF;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import java.lang.ref.WeakReference;
import java.util.function.Consumer;
/**
@@ -86,8 +92,10 @@ public class InlineContentView extends ViewGroup {
*
* @hide
*/
+ @TestApi
public interface SurfacePackageUpdater {
+
/**
* Called when the previous surface package is released due to view being detached
* from the window.
@@ -99,14 +107,16 @@ public class InlineContentView extends ViewGroup {
*
* @param consumer consumes the updated surface package.
*/
- void getSurfacePackage(Consumer consumer);
+ void getSurfacePackage(@NonNull Consumer consumer);
}
@NonNull
private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
- mSurfaceControlCallback.onCreated(mSurfaceView.getSurfaceControl());
+ final SurfaceControl surfaceControl = mSurfaceView.getSurfaceControl();
+ surfaceControl.addOnReparentListener(mOnReparentListener);
+ mSurfaceControlCallback.onCreated(surfaceControl);
}
@Override
@@ -117,13 +127,52 @@ public class InlineContentView extends ViewGroup {
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
- mSurfaceControlCallback.onDestroyed(mSurfaceView.getSurfaceControl());
+ final SurfaceControl surfaceControl = mSurfaceView.getSurfaceControl();
+ surfaceControl.removeOnReparentListener(mOnReparentListener);
+ mSurfaceControlCallback.onDestroyed(surfaceControl);
+ }
+ };
+
+ @NonNull
+ private final SurfaceControl.OnReparentListener mOnReparentListener =
+ new SurfaceControl.OnReparentListener() {
+ @Override
+ public void onReparent(SurfaceControl.Transaction transaction,
+ SurfaceControl parent) {
+ final View parentSurfaceOwnerView = (parent != null)
+ ? parent.getLocalOwnerView() : null;
+ if (parentSurfaceOwnerView instanceof SurfaceView) {
+ mParentSurfaceOwnerView = new WeakReference<>(
+ (SurfaceView) parentSurfaceOwnerView);
+ } else {
+ mParentSurfaceOwnerView = null;
+ }
+ }
+ };
+
+ @NonNull
+ private final ViewTreeObserver.OnDrawListener mOnDrawListener =
+ new ViewTreeObserver.OnDrawListener() {
+ @Override
+ public void onDraw() {
+ computeParentPositionAndScale();
+ final int visibility = InlineContentView.this.isShown() ? VISIBLE : GONE;
+ mSurfaceView.setVisibility(visibility);
}
};
@NonNull
private final SurfaceView mSurfaceView;
+ @Nullable
+ private WeakReference mParentSurfaceOwnerView;
+
+ @Nullable
+ private int[] mParentPosition;
+
+ @Nullable
+ private PointF mParentScale;
+
@Nullable
private SurfaceControlCallback mSurfaceControlCallback;
@@ -153,6 +202,7 @@ public class InlineContentView extends ViewGroup {
public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
+ mSurfaceView.setEnableSurfaceClipping(true);
}
/**
@@ -166,6 +216,12 @@ public class InlineContentView extends ViewGroup {
return mSurfaceView.getSurfaceControl();
}
+ @Override
+ public void setClipBounds(Rect clipBounds) {
+ super.setClipBounds(clipBounds);
+ mSurfaceView.setClipBounds(clipBounds);
+ }
+
/**
* @inheritDoc
* @hide
@@ -173,10 +229,33 @@ public class InlineContentView extends ViewGroup {
public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes);
+ mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes) {
+ @Override
+ protected void onSetSurfacePositionAndScaleRT(
+ @NonNull SurfaceControl.Transaction transaction,
+ @NonNull SurfaceControl surface, int positionLeft, int positionTop,
+ float postScaleX, float postScaleY) {
+ // If we have a parent position, we need to make our coordinates relative
+ // to the parent in the rendering space.
+ if (mParentPosition != null) {
+ positionLeft = (int) ((positionLeft - mParentPosition[0]) / mParentScale.x);
+ positionTop = (int) ((positionTop - mParentPosition[1]) / mParentScale.y);
+ }
+
+ // Any scaling done to the parent or its predecessors would be applied
+ // via the surfaces parent -> child relation, so we only propagate any
+ // scaling set on the InlineContentView itself.
+ postScaleX = InlineContentView.this.getScaleX();
+ postScaleY = InlineContentView.this.getScaleY();
+
+ super.onSetSurfacePositionAndScaleRT(transaction, surface, positionLeft,
+ positionTop, postScaleX, postScaleY);
+ }
+ };
mSurfaceView.setZOrderOnTop(true);
mSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
addView(mSurfaceView);
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
/**
@@ -184,6 +263,7 @@ public class InlineContentView extends ViewGroup {
*
* @hide
*/
+ @TestApi
public void setChildSurfacePackageUpdater(
@Nullable SurfacePackageUpdater surfacePackageUpdater) {
mSurfacePackageUpdater = surfacePackageUpdater;
@@ -197,9 +277,14 @@ public class InlineContentView extends ViewGroup {
mSurfacePackageUpdater.getSurfacePackage(
sp -> {
if (DEBUG) Log.v(TAG, "Received new SurfacePackage");
- mSurfaceView.setChildSurfacePackage(sp);
+ if (getViewRootImpl() != null) {
+ mSurfaceView.setChildSurfacePackage(sp);
+ }
});
}
+
+ mSurfaceView.setVisibility(getVisibility());
+ getViewTreeObserver().addOnDrawListener(mOnDrawListener);
}
@Override
@@ -209,6 +294,9 @@ public class InlineContentView extends ViewGroup {
if (mSurfacePackageUpdater != null) {
mSurfacePackageUpdater.onSurfacePackageReleased();
}
+
+ getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
+ mSurfaceView.setVisibility(View.GONE);
}
@Override
@@ -255,4 +343,67 @@ public class InlineContentView extends ViewGroup {
public boolean setZOrderedOnTop(boolean onTop) {
return mSurfaceView.setZOrderedOnTop(onTop, /*allowDynamicChange*/ true);
}
+
+
+ private void computeParentPositionAndScale() {
+ boolean contentPositionOrScaleChanged = false;
+
+ // This method can be called on the UI or render thread but for the cases
+ // it is called these threads are not running concurrently, so no need to lock.
+ final SurfaceView parentSurfaceOwnerView = (mParentSurfaceOwnerView != null)
+ ? mParentSurfaceOwnerView.get() : null;
+
+ if (parentSurfaceOwnerView != null) {
+ if (mParentPosition == null) {
+ mParentPosition = new int[2];
+ }
+ final int oldParentPositionX = mParentPosition[0];
+ final int oldParentPositionY = mParentPosition[1];
+ parentSurfaceOwnerView.getLocationInSurface(mParentPosition);
+ if (oldParentPositionX != mParentPosition[0]
+ || oldParentPositionY != mParentPosition[1]) {
+ contentPositionOrScaleChanged = true;
+ }
+
+ if (mParentScale == null) {
+ mParentScale = new PointF();
+ }
+
+ final float lastParentSurfaceWidth = parentSurfaceOwnerView
+ .getSurfaceRenderPosition().width();
+ final float oldParentScaleX = mParentScale.x;
+ if (lastParentSurfaceWidth > 0) {
+ mParentScale.x = lastParentSurfaceWidth /
+ (float) parentSurfaceOwnerView.getWidth();
+ } else {
+ mParentScale.x = 1.0f;
+ }
+ if (!contentPositionOrScaleChanged
+ && Float.compare(oldParentScaleX, mParentScale.x) != 0) {
+ contentPositionOrScaleChanged = true;
+ }
+
+ final float lastParentSurfaceHeight = parentSurfaceOwnerView
+ .getSurfaceRenderPosition().height();
+ final float oldParentScaleY = mParentScale.y;
+ if (lastParentSurfaceHeight > 0) {
+ mParentScale.y = lastParentSurfaceHeight
+ / (float) parentSurfaceOwnerView.getHeight();
+ } else {
+ mParentScale.y = 1.0f;
+ }
+ if (!contentPositionOrScaleChanged
+ && Float.compare(oldParentScaleY, mParentScale.y) != 0) {
+ contentPositionOrScaleChanged = true;
+ }
+ } else if (mParentPosition != null || mParentScale != null) {
+ contentPositionOrScaleChanged = true;
+ mParentPosition = null;
+ mParentScale = null;
+ }
+
+ if (contentPositionOrScaleChanged) {
+ mSurfaceView.requestUpdateSurfacePositionAndScale();
+ }
+ }
}
diff --git a/core/java/android/window/TaskEmbedder.java b/core/java/android/window/TaskEmbedder.java
index ca6c568c2668c784d6f5a733b6c7d07b72fe354d..0687a037df32a48828baca5c90529052eab98310 100644
--- a/core/java/android/window/TaskEmbedder.java
+++ b/core/java/android/window/TaskEmbedder.java
@@ -164,6 +164,7 @@ public abstract class TaskEmbedder {
.setContainerLayer()
.setParent(parent)
.setName(name)
+ .setCallsite("TaskEmbedder.initialize")
.build();
if (!onInitialize()) {
diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index d2614da31ff9d8514f6680ea065c6cf09c7dead1..9ccb4c172158d5678523dc339f55e051ae97b88f 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -365,8 +365,8 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder {
// Found the topmost stack on target display. Now check if the topmost task's
// description changed.
if (taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
- mHost.onTaskBackgroundColorChanged(VirtualDisplayTaskEmbedder.this,
- taskInfo.taskDescription.getBackgroundColor());
+ mHost.post(()-> mHost.onTaskBackgroundColorChanged(VirtualDisplayTaskEmbedder.this,
+ taskInfo.taskDescription.getBackgroundColor()));
}
}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
index 1b7517840650c94f386246009e48917d7404d6a6..508deacb49d7a8c105be51f48c2a46cafe6a2100 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
@@ -23,6 +23,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.Shortc
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.createEnableDialogContentView;
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getInstalledTargets;
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
+import static com.android.internal.accessibility.util.AccessibilityUtils.isUserSetupCompleted;
import android.annotation.Nullable;
import android.app.Activity;
@@ -61,18 +62,8 @@ public class AccessibilityShortcutChooserActivity extends Activity {
}
mTargets.addAll(getTargets(this, mShortcutType));
-
- final String selectDialogTitle =
- getString(R.string.accessibility_select_shortcut_menu_title);
mTargetAdapter = new ShortcutTargetAdapter(mTargets);
- mMenuDialog = new AlertDialog.Builder(this)
- .setTitle(selectDialogTitle)
- .setAdapter(mTargetAdapter, /* listener= */ null)
- .setPositiveButton(
- getString(R.string.edit_accessibility_shortcut_menu_button),
- /* listener= */ null)
- .setOnDismissListener(dialog -> finish())
- .create();
+ mMenuDialog = createMenuDialog();
mMenuDialog.setOnShowListener(dialog -> updateDialogListeners());
mMenuDialog.show();
}
@@ -154,4 +145,22 @@ public class AccessibilityShortcutChooserActivity extends Activity {
mMenuDialog.getListView().setOnItemClickListener(
isEditMenuMode ? this::onTargetChecked : this::onTargetSelected);
}
+
+ private AlertDialog createMenuDialog() {
+ final String dialogTitle =
+ getString(R.string.accessibility_select_shortcut_menu_title);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(dialogTitle)
+ .setAdapter(mTargetAdapter, /* listener= */ null)
+ .setOnDismissListener(dialog -> finish());
+
+ if (isUserSetupCompleted(this)) {
+ final String positiveButtonText =
+ getString(R.string.edit_accessibility_shortcut_menu_button);
+ builder.setPositiveButton(positiveButtonText, /* listener= */ null);
+ }
+
+ return builder.create();
+ }
}
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
index 9ee0b0ea18910d62a9a26d822bff249a9bf66343..4b4e20f9181b5df4c4d0adc13ef8f26b21f102d4 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
@@ -156,4 +156,16 @@ public final class AccessibilityUtils {
return false;
}
+
+ /**
+ * Indicates whether the current user has completed setup via the setup wizard.
+ * {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE}
+ *
+ * @return {@code true} if the setup is completed.
+ */
+ public static boolean isUserSetupCompleted(Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, /* def= */ 0, UserHandle.USER_CURRENT)
+ != /* false */ 0;
+ }
}
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index 493865ac563f905da4c8705dac05f35340d1d106..b723db28782320f05515a42b0f7b98bc4f100950 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -151,6 +151,13 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
mOnProfileSelectedListener.onProfileSelected(position);
}
}
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ if (mOnProfileSelectedListener != null) {
+ mOnProfileSelectedListener.onProfilePageStateChanged(state);
+ }
+ }
});
viewPager.setAdapter(this);
viewPager.setCurrentItem(mCurrentPage);
@@ -606,6 +613,17 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
* {@link #PROFILE_WORK} if the work profile was selected.
*/
void onProfileSelected(int profileIndex);
+
+
+ /**
+ * Callback for when the scroll state changes. Useful for discovering when the user begins
+ * dragging, when the pager is automatically settling to the current page, or when it is
+ * fully stopped/idle.
+ * @param state {@link ViewPager#SCROLL_STATE_IDLE}, {@link ViewPager#SCROLL_STATE_DRAGGING}
+ * or {@link ViewPager#SCROLL_STATE_SETTLING}
+ * @see ViewPager.OnPageChangeListener#onPageScrollStateChanged
+ */
+ void onProfilePageStateChanged(int state);
}
/**
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5533e1eda52d35b593056437719160074a286f23..14cf258f18ab29495335a0f8906dde8d65dabeaa 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -102,6 +102,7 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.Button;
@@ -129,6 +130,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.GridLayoutManager;
import com.android.internal.widget.RecyclerView;
import com.android.internal.widget.ResolverDrawerLayout;
+import com.android.internal.widget.ViewPager;
import com.google.android.collect.Lists;
@@ -193,6 +195,7 @@ public class ChooserActivity extends ResolverActivity implements
private boolean mIsAppPredictorComponentAvailable;
private Map mDirectShareAppTargetCache;
private Map mDirectShareShortcutInfoCache;
+ private Map mChooserTargetComponentNameCache;
public static final int TARGET_TYPE_DEFAULT = 0;
public static final int TARGET_TYPE_CHOOSER_TARGET = 1;
@@ -204,6 +207,10 @@ public class ChooserActivity extends ResolverActivity implements
public static final int SELECTION_TYPE_STANDARD = 3;
public static final int SELECTION_TYPE_COPY = 4;
+ private static final int SCROLL_STATUS_IDLE = 0;
+ private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1;
+ private static final int SCROLL_STATUS_SCROLLING_HORIZONTAL = 2;
+
// statsd logger wrapper
protected ChooserActivityLogger mChooserActivityLogger;
@@ -293,6 +300,7 @@ public class ChooserActivity extends ResolverActivity implements
protected MetricsLogger mMetricsLogger;
private ContentPreviewCoordinator mPreviewCoord;
+ private int mScrollStatus = SCROLL_STATUS_IDLE;
@VisibleForTesting
protected ChooserMultiProfilePagerAdapter mChooserMultiProfilePagerAdapter;
@@ -504,6 +512,11 @@ public class ChooserActivity extends ResolverActivity implements
adapterForUserHandle.addServiceResults(sri.originalTarget,
sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET,
/* directShareShortcutInfoCache */ null, mServiceConnections);
+ if (!sri.resultTargets.isEmpty() && sri.originalTarget != null) {
+ mChooserTargetComponentNameCache.put(
+ sri.resultTargets.get(0).getComponentName(),
+ sri.originalTarget.getResolvedComponentName());
+ }
}
}
unbindService(sri.connection);
@@ -678,8 +691,14 @@ public class ChooserActivity extends ResolverActivity implements
mPinnedSharedPrefs = getPinnedSharedPrefs(this);
pa = intent.getParcelableArrayExtra(Intent.EXTRA_EXCLUDE_COMPONENTS);
+
+
+ // Exclude out Nearby from main list if chip is present, to avoid duplication
+ ComponentName nearbySharingComponent = getNearbySharingComponent();
+ boolean hasNearby = nearbySharingComponent != null;
+
if (pa != null) {
- ComponentName[] names = new ComponentName[pa.length];
+ ComponentName[] names = new ComponentName[pa.length + (hasNearby ? 1 : 0)];
for (int i = 0; i < pa.length; i++) {
if (!(pa[i] instanceof ComponentName)) {
Log.w(TAG, "Filtered component #" + i + " not a ComponentName: " + pa[i]);
@@ -688,7 +707,14 @@ public class ChooserActivity extends ResolverActivity implements
}
names[i] = (ComponentName) pa[i];
}
+ if (hasNearby) {
+ names[names.length - 1] = nearbySharingComponent;
+ }
+
mFilteredComponentNames = names;
+ } else if (hasNearby) {
+ mFilteredComponentNames = new ComponentName[1];
+ mFilteredComponentNames[0] = nearbySharingComponent;
}
pa = intent.getParcelableArrayExtra(Intent.EXTRA_CHOOSER_TARGETS);
@@ -765,6 +791,7 @@ public class ChooserActivity extends ResolverActivity implements
target.getAction()
);
mDirectShareShortcutInfoCache = new HashMap<>();
+ mChooserTargetComponentNameCache = new HashMap<>();
}
@Override
@@ -786,17 +813,15 @@ public class ChooserActivity extends ResolverActivity implements
private AppPredictor.Callback createAppPredictorCallback(
ChooserListAdapter chooserListAdapter) {
return resultList -> {
- //TODO(arangelov) Take care of edge case when callback called after swiping tabs
if (isFinishing() || isDestroyed()) {
return;
}
if (chooserListAdapter.getCount() == 0) {
return;
}
- if (resultList.isEmpty()) {
+ if (resultList.isEmpty()
+ && shouldQueryShortcutManager(chooserListAdapter.getUserHandle())) {
// APS may be disabled, so try querying targets ourselves.
- //TODO(arangelov) queryDirectShareTargets indirectly uses mIntents.
- // Investigate implications for work tab.
queryDirectShareTargets(chooserListAdapter, true);
return;
}
@@ -993,11 +1018,17 @@ public class ChooserActivity extends ResolverActivity implements
/**
* Update UI to reflect changes in data.
- *
If {@code listAdapter} is {@code null}, both profile list adapters are updated.
+ *
If {@code listAdapter} is {@code null}, both profile list adapters are updated if
+ * available.
*/
private void handlePackagesChanged(@Nullable ResolverListAdapter listAdapter) {
+ // Refresh pinned items
+ mPinnedSharedPrefs = getPinnedSharedPrefs(this);
if (listAdapter == null) {
mChooserMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
+ if (mChooserMultiProfilePagerAdapter.getCount() > 1) {
+ mChooserMultiProfilePagerAdapter.getInactiveListAdapter().handlePackagesChanged();
+ }
} else {
listAdapter.handlePackagesChanged();
}
@@ -1059,6 +1090,10 @@ public class ChooserActivity extends ResolverActivity implements
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ ViewPager viewPager = findViewById(R.id.profile_pager);
+ if (shouldShowTabs() && viewPager.isLayoutRtl()) {
+ mMultiProfilePagerAdapter.setupViewPager(viewPager);
+ }
mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation);
adjustPreviewWidth(newConfig.orientation, null);
@@ -1117,7 +1152,7 @@ public class ChooserActivity extends ResolverActivity implements
final ComponentName cn = getNearbySharingComponent();
if (cn == null) return null;
- final Intent resolveIntent = new Intent();
+ final Intent resolveIntent = new Intent(originalIntent);
resolveIntent.setComponent(cn);
final ResolveInfo ri = getPackageManager().resolveActivity(
resolveIntent, PackageManager.GET_META_DATA);
@@ -1281,6 +1316,12 @@ public class ChooserActivity extends ResolverActivity implements
ViewGroup parent) {
ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(
R.layout.chooser_grid_preview_image, parent, false);
+
+ final ViewGroup actionRow =
+ (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row);
+ //TODO: addActionButton(actionRow, createCopyButton());
+ addActionButton(actionRow, createNearbyButton(targetIntent));
+
mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, true);
String action = targetIntent.getAction();
@@ -1391,10 +1432,11 @@ public class ChooserActivity extends ResolverActivity implements
ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(
R.layout.chooser_grid_preview_file, parent, false);
- // TODO(b/120417119): Disable file copy until after moving to sysui,
- // due to permissions issues
- //((ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row))
- // .addView(createCopyButton());
+ final ViewGroup actionRow =
+ (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row);
+ //TODO(b/120417119): addActionButton(actionRow, createCopyButton());
+ addActionButton(actionRow, createNearbyButton(targetIntent));
+
String action = targetIntent.getAction();
if (Intent.ACTION_SEND.equals(action)) {
@@ -1802,7 +1844,8 @@ public class ChooserActivity extends ResolverActivity implements
}
}
- void queryTargetServices(ChooserListAdapter adapter) {
+ @VisibleForTesting
+ protected void queryTargetServices(ChooserListAdapter adapter) {
mQueriedTargetServicesTimeMs = System.currentTimeMillis();
Context selectedProfileContext = createContextAsUser(
@@ -1955,7 +1998,8 @@ public class ChooserActivity extends ResolverActivity implements
return driList;
}
- private void queryDirectShareTargets(
+ @VisibleForTesting
+ protected void queryDirectShareTargets(
ChooserListAdapter adapter, boolean skipAppPredictionService) {
mQueriedSharingShortcutsTimeMs = System.currentTimeMillis();
UserHandle userHandle = adapter.getUserHandle();
@@ -1967,7 +2011,6 @@ public class ChooserActivity extends ResolverActivity implements
}
}
// Default to just querying ShortcutManager if AppPredictor not present.
- //TODO(arangelov) we're using mIntents here, investicate possible implications on work tab
final IntentFilter filter = getTargetIntentFilter();
if (filter == null) {
return;
@@ -1983,6 +2026,29 @@ public class ChooserActivity extends ResolverActivity implements
});
}
+ /**
+ * Returns {@code false} if {@code userHandle} is the work profile and it's either
+ * in quiet mode or not running.
+ */
+ private boolean shouldQueryShortcutManager(UserHandle userHandle) {
+ if (!shouldShowTabs()) {
+ return true;
+ }
+ if (!getWorkProfileUserHandle().equals(userHandle)) {
+ return true;
+ }
+ if (!isUserRunning(userHandle)) {
+ return false;
+ }
+ if (!isUserUnlocked(userHandle)) {
+ return false;
+ }
+ if (isQuietModeEnabled(userHandle)) {
+ return false;
+ }
+ return true;
+ }
+
private void sendChooserTargetRankingScore(List chooserTargetScores,
UserHandle userHandle) {
final Message msg = Message.obtain();
@@ -2182,6 +2248,9 @@ public class ChooserActivity extends ResolverActivity implements
}
void updateModelAndChooserCounts(TargetInfo info) {
+ if (info != null && info instanceof MultiDisplayResolveInfo) {
+ info = ((MultiDisplayResolveInfo) info).getSelectedTarget();
+ }
if (info != null) {
sendClickToAppPredictor(info);
final ResolveInfo ri = info.getResolveInfo();
@@ -2223,15 +2292,18 @@ public class ChooserActivity extends ResolverActivity implements
List targetIds = new ArrayList<>();
for (ChooserTargetInfo chooserTargetInfo : surfacedTargetInfo) {
ChooserTarget chooserTarget = chooserTargetInfo.getChooserTarget();
- String componentName = chooserTarget.getComponentName().flattenToString();
+ ComponentName componentName = mChooserTargetComponentNameCache.getOrDefault(
+ chooserTarget.getComponentName(), chooserTarget.getComponentName());
if (mDirectShareShortcutInfoCache.containsKey(chooserTarget)) {
String shortcutId = mDirectShareShortcutInfoCache.get(chooserTarget).getId();
targetIds.add(new AppTargetId(
- String.format("%s/%s/%s", shortcutId, componentName, SHORTCUT_TARGET)));
+ String.format("%s/%s/%s", shortcutId, componentName.flattenToString(),
+ SHORTCUT_TARGET)));
} else {
String titleHash = ChooserUtil.md5(chooserTarget.getTitle().toString());
targetIds.add(new AppTargetId(
- String.format("%s/%s/%s", titleHash, componentName, CHOOSER_TARGET)));
+ String.format("%s/%s/%s", titleHash, componentName.flattenToString(),
+ CHOOSER_TARGET)));
}
}
directShareAppPredictor.notifyLaunchLocationShown(LAUNCH_LOCATION_DIRECT_SHARE, targetIds);
@@ -2253,7 +2325,8 @@ public class ChooserActivity extends ResolverActivity implements
}
if (mChooserTargetRankingEnabled && appTarget == null) {
// Send ChooserTarget sharing info to AppPredictor.
- ComponentName componentName = chooserTarget.getComponentName();
+ ComponentName componentName = mChooserTargetComponentNameCache.getOrDefault(
+ chooserTarget.getComponentName(), chooserTarget.getComponentName());
try {
appTarget = new AppTarget.Builder(
new AppTargetId(componentName.flattenToString()),
@@ -2644,6 +2717,7 @@ public class ChooserActivity extends ResolverActivity implements
if (recyclerView.getVisibility() == View.VISIBLE) {
int directShareHeight = 0;
rowsToShow = Math.min(4, rowsToShow);
+ boolean shouldShowExtraRow = shouldShowExtraRow(rowsToShow);
mLastNumberOfChildren = recyclerView.getChildCount();
for (int i = 0, childCount = recyclerView.getChildCount();
i < childCount && rowsToShow > 0; i++) {
@@ -2654,6 +2728,9 @@ public class ChooserActivity extends ResolverActivity implements
}
int height = child.getHeight();
offset += height;
+ if (shouldShowExtraRow) {
+ offset += height;
+ }
if (gridAdapter.getTargetType(
recyclerView.getChildAdapterPosition(child))
@@ -2677,7 +2754,7 @@ public class ChooserActivity extends ResolverActivity implements
offset = Math.min(offset, minHeight);
}
} else {
- ViewGroup currentEmptyStateView = getCurrentEmptyStateView();
+ ViewGroup currentEmptyStateView = getActiveEmptyStateView();
if (currentEmptyStateView.getVisibility() == View.VISIBLE) {
offset += currentEmptyStateView.getHeight();
}
@@ -2688,6 +2765,18 @@ public class ChooserActivity extends ResolverActivity implements
}
}
+ /**
+ * If we have a tabbed view and are showing 1 row in the current profile and an empty
+ * state screen in the other profile, to prevent cropping of the empty state screen we show
+ * a second row in the current profile.
+ */
+ private boolean shouldShowExtraRow(int rowsToShow) {
+ return shouldShowTabs()
+ && rowsToShow == 1
+ && mChooserMultiProfilePagerAdapter.shouldShowEmptyStateScreen(
+ mChooserMultiProfilePagerAdapter.getInactiveListAdapter());
+ }
+
/**
* Returns {@link #PROFILE_PERSONAL}, {@link #PROFILE_WORK}, or -1 if the given user handle
* does not match either the personal or work user handle.
@@ -2702,7 +2791,7 @@ public class ChooserActivity extends ResolverActivity implements
return -1;
}
- private ViewGroup getCurrentEmptyStateView() {
+ private ViewGroup getActiveEmptyStateView() {
int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage();
return mChooserMultiProfilePagerAdapter.getItem(currentPage).getEmptyStateView();
}
@@ -2765,17 +2854,7 @@ public class ChooserActivity extends ResolverActivity implements
|| chooserListAdapter.mDisplayList.isEmpty()) {
chooserListAdapter.notifyDataSetChanged();
} else {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... voids) {
- chooserListAdapter.updateAlphabeticalList();
- return null;
- }
- @Override
- protected void onPostExecute(Void aVoid) {
- chooserListAdapter.notifyDataSetChanged();
- }
- }.execute();
+ chooserListAdapter.updateAlphabeticalList();
}
// don't support direct share on low ram devices
@@ -2784,6 +2863,12 @@ public class ChooserActivity extends ResolverActivity implements
return;
}
+ // no need to query direct share for work profile when its locked or disabled
+ if (!shouldQueryShortcutManager(chooserListAdapter.getUserHandle())) {
+ getChooserActivityLogger().logSharesheetAppLoadComplete();
+ return;
+ }
+
if (ChooserFlags.USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS
|| ChooserFlags.USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) {
if (DEBUG) {
@@ -2803,6 +2888,24 @@ public class ChooserActivity extends ResolverActivity implements
getChooserActivityLogger().logSharesheetAppLoadComplete();
}
+ @VisibleForTesting
+ protected boolean isUserRunning(UserHandle userHandle) {
+ UserManager userManager = getSystemService(UserManager.class);
+ return userManager.isUserRunning(userHandle);
+ }
+
+ @VisibleForTesting
+ protected boolean isUserUnlocked(UserHandle userHandle) {
+ UserManager userManager = getSystemService(UserManager.class);
+ return userManager.isUserUnlocked(userHandle);
+ }
+
+ @VisibleForTesting
+ protected boolean isQuietModeEnabled(UserHandle userHandle) {
+ UserManager userManager = getSystemService(UserManager.class);
+ return userManager.isQuietModeEnabled(userHandle);
+ }
+
private void setupScrollListener() {
if (mResolverDrawerLayout == null) {
return;
@@ -2812,10 +2915,20 @@ public class ChooserActivity extends ResolverActivity implements
final float defaultElevation = elevatedView.getElevation();
final float chooserHeaderScrollElevation =
getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation);
-
mChooserMultiProfilePagerAdapter.getActiveAdapterView().addOnScrollListener(
new RecyclerView.OnScrollListener() {
public void onScrollStateChanged(RecyclerView view, int scrollState) {
+ if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
+ if (mScrollStatus == SCROLL_STATUS_SCROLLING_VERTICAL) {
+ mScrollStatus = SCROLL_STATUS_IDLE;
+ setHorizontalScrollingEnabled(true);
+ }
+ } else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) {
+ if (mScrollStatus == SCROLL_STATUS_IDLE) {
+ mScrollStatus = SCROLL_STATUS_SCROLLING_VERTICAL;
+ setHorizontalScrollingEnabled(false);
+ }
+ }
}
public void onScrolled(RecyclerView view, int dx, int dy) {
@@ -3016,6 +3129,44 @@ public class ChooserActivity extends ResolverActivity implements
currentRootAdapter.updateDirectShareExpansion();
}
+ @Override
+ protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ if (shouldShowTabs()) {
+ mChooserMultiProfilePagerAdapter
+ .setEmptyStateBottomOffset(insets.getSystemWindowInsetBottom());
+ mChooserMultiProfilePagerAdapter.setupContainerPadding(
+ getActiveEmptyStateView().findViewById(R.id.resolver_empty_state_container));
+ }
+ return super.onApplyWindowInsets(v, insets);
+ }
+
+ private void setHorizontalScrollingEnabled(boolean enabled) {
+ ResolverViewPager viewPager = findViewById(R.id.profile_pager);
+ viewPager.setSwipingEnabled(enabled);
+ }
+
+ private void setVerticalScrollEnabled(boolean enabled) {
+ ChooserGridLayoutManager layoutManager =
+ (ChooserGridLayoutManager) mChooserMultiProfilePagerAdapter.getActiveAdapterView()
+ .getLayoutManager();
+ layoutManager.setVerticalScrollEnabled(enabled);
+ }
+
+ @Override
+ void onHorizontalSwipeStateChanged(int state) {
+ if (state == ViewPager.SCROLL_STATE_DRAGGING) {
+ if (mScrollStatus == SCROLL_STATUS_IDLE) {
+ mScrollStatus = SCROLL_STATUS_SCROLLING_HORIZONTAL;
+ setVerticalScrollEnabled(false);
+ }
+ } else if (state == ViewPager.SCROLL_STATE_IDLE) {
+ if (mScrollStatus == SCROLL_STATUS_SCROLLING_HORIZONTAL) {
+ mScrollStatus = SCROLL_STATUS_IDLE;
+ setVerticalScrollEnabled(true);
+ }
+ }
+ }
+
/**
* Adapter for all types of items and targets in ShareSheet.
* Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
@@ -3506,10 +3657,9 @@ public class ChooserActivity extends ResolverActivity implements
* Only expand direct share area if there is a minimum number of targets.
*/
private boolean canExpandDirectShare() {
- int orientation = getResources().getConfiguration().orientation;
- return mChooserListAdapter.getNumServiceTargetsForExpand() > getMaxTargetsPerRow()
- && orientation == Configuration.ORIENTATION_PORTRAIT
- && !isInMultiWindowMode();
+ // Do not enable until we have confirmed more apps are using sharing shortcuts
+ // Check git history for enablement logic
+ return false;
}
public ChooserListAdapter getListAdapter() {
diff --git a/core/java/com/android/internal/app/ChooserGridLayoutManager.java b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
index 317a987cf3599cc7c15bc802029019ee0ec9273e..c50ebd9562c99780c619f182d9af59f0f548aed0 100644
--- a/core/java/com/android/internal/app/ChooserGridLayoutManager.java
+++ b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
@@ -28,6 +28,8 @@ import com.android.internal.widget.RecyclerView;
*/
public class ChooserGridLayoutManager extends GridLayoutManager {
+ private boolean mVerticalScrollEnabled = true;
+
/**
* Constructor used when layout manager is set in XML by RecyclerView attribute
* "layoutManager". If spanCount is not specified in the XML, it defaults to a
@@ -67,4 +69,13 @@ public class ChooserGridLayoutManager extends GridLayoutManager {
// Do not count the footer view in the official count
return super.getRowCountForAccessibility(recycler, state) - 1;
}
+
+ void setVerticalScrollEnabled(boolean verticalScrollEnabled) {
+ mVerticalScrollEnabled = verticalScrollEnabled;
+ }
+
+ @Override
+ public boolean canScrollVertically() {
+ return mVerticalScrollEnabled && super.canScrollVertically();
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index f4fb993fbb9322dee12e9e18e2fce1c5ed1a784b..00b5cb646bca2b9199b792ddca392b406a9b67e4 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -95,6 +95,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
mSelectableTargetInfoCommunicator;
private int mNumShortcutResults = 0;
+ private Map mIconLoaders = new HashMap<>();
// Reserve spots for incoming direct share targets by adding placeholders
private ChooserTargetInfo
@@ -181,6 +182,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
ri.icon = 0;
}
mCallerTargets.add(new DisplayResolveInfo(ii, ri, ii, makePresentationGetter(ri)));
+ if (mCallerTargets.size() == MAX_SUGGESTED_APP_TARGETS) break;
}
}
}
@@ -238,11 +240,42 @@ public class ChooserListAdapter extends ResolverListAdapter {
@Override
protected void onBindView(View view, TargetInfo info, int position) {
- super.onBindView(view, info, position);
- if (info == null) return;
+ final ViewHolder holder = (ViewHolder) view.getTag();
+ if (info == null) {
+ holder.icon.setImageDrawable(
+ mContext.getDrawable(R.drawable.resolver_icon_placeholder));
+ return;
+ }
+
+ if (!(info instanceof DisplayResolveInfo)) {
+ holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel());
+ holder.bindIcon(info);
+
+ if (info instanceof SelectableTargetInfo) {
+ // direct share targets should append the application name for a better readout
+ DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo();
+ CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
+ CharSequence extendedInfo = info.getExtendedInfo();
+ String contentDescription = String.join(" ", info.getDisplayLabel(),
+ extendedInfo != null ? extendedInfo : "", appName);
+ holder.updateContentDescription(contentDescription);
+ }
+ } else {
+ DisplayResolveInfo dri = (DisplayResolveInfo) info;
+ holder.bindLabel(dri.getDisplayLabel(), dri.getExtendedInfo(), alwaysShowSubLabel());
+ LoadIconTask task = mIconLoaders.get(dri);
+ if (task == null) {
+ task = new LoadIconTask(dri, holder);
+ mIconLoaders.put(dri, task);
+ task.execute();
+ } else {
+ // The holder was potentially changed as the underlying items were
+ // reshuffled, so reset the target holder
+ task.setViewHolder(holder);
+ }
+ }
// If target is loading, show a special placeholder shape in the label, make unclickable
- final ViewHolder holder = (ViewHolder) view.getTag();
if (info instanceof ChooserActivity.PlaceHolderTargetInfo) {
final int maxWidth = mContext.getResources().getDimensionPixelSize(
R.dimen.chooser_direct_share_label_placeholder_max_width);
@@ -274,33 +307,43 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
void updateAlphabeticalList() {
- mSortedList.clear();
- List tempList = new ArrayList<>();
- tempList.addAll(mDisplayList);
- tempList.addAll(mCallerTargets);
- if (mEnableStackedApps) {
- // Consolidate multiple targets from same app.
- Map consolidated = new HashMap<>();
- for (DisplayResolveInfo info : tempList) {
- String packageName = info.getResolvedComponentName().getPackageName();
- DisplayResolveInfo multiDri = consolidated.get(packageName);
- if (multiDri == null) {
- consolidated.put(packageName, info);
- } else if (multiDri instanceof MultiDisplayResolveInfo) {
- ((MultiDisplayResolveInfo) multiDri).addTarget(info);
- } else {
- // create consolidated target from the single DisplayResolveInfo
- MultiDisplayResolveInfo multiDisplayResolveInfo =
+ new AsyncTask>() {
+ @Override
+ protected List doInBackground(Void... voids) {
+ List allTargets = new ArrayList<>();
+ allTargets.addAll(mDisplayList);
+ allTargets.addAll(mCallerTargets);
+ if (!mEnableStackedApps) {
+ return allTargets;
+ }
+ // Consolidate multiple targets from same app.
+ Map consolidated = new HashMap<>();
+ for (DisplayResolveInfo info : allTargets) {
+ String packageName = info.getResolvedComponentName().getPackageName();
+ DisplayResolveInfo multiDri = consolidated.get(packageName);
+ if (multiDri == null) {
+ consolidated.put(packageName, info);
+ } else if (multiDri instanceof MultiDisplayResolveInfo) {
+ ((MultiDisplayResolveInfo) multiDri).addTarget(info);
+ } else {
+ // create consolidated target from the single DisplayResolveInfo
+ MultiDisplayResolveInfo multiDisplayResolveInfo =
new MultiDisplayResolveInfo(packageName, multiDri);
- multiDisplayResolveInfo.addTarget(info);
- consolidated.put(packageName, multiDisplayResolveInfo);
+ multiDisplayResolveInfo.addTarget(info);
+ consolidated.put(packageName, multiDisplayResolveInfo);
+ }
}
+ List groupedTargets = new ArrayList<>();
+ groupedTargets.addAll(consolidated.values());
+ Collections.sort(groupedTargets, new ChooserActivity.AzInfoComparator(mContext));
+ return groupedTargets;
}
- mSortedList.addAll(consolidated.values());
- } else {
- mSortedList.addAll(tempList);
- }
- Collections.sort(mSortedList, new ChooserActivity.AzInfoComparator(mContext));
+ @Override
+ protected void onPostExecute(List newList) {
+ mSortedList = newList;
+ notifyDataSetChanged();
+ }
+ }.execute();
}
@Override
@@ -320,7 +363,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
public int getCallerTargetCount() {
- return Math.min(mCallerTargets.size(), MAX_SUGGESTED_APP_TARGETS);
+ return mCallerTargets.size();
}
/**
@@ -346,8 +389,9 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
int getAlphaTargetCount() {
- int standardCount = mSortedList.size();
- return standardCount > mChooserListCommunicator.getMaxRankedTargets() ? standardCount : 0;
+ int groupedCount = mSortedList.size();
+ int ungroupedCount = mCallerTargets.size() + mDisplayList.size();
+ return ungroupedCount > mChooserListCommunicator.getMaxRankedTargets() ? groupedCount : 0;
}
/**
@@ -549,7 +593,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
mChooserTargetScores.put(componentName, new HashMap<>());
}
mChooserTargetScores.get(componentName).put(shortcutInfo.getShortLabel().toString(),
- shortcutInfo.getRank());
+ target.getRank());
}
mChooserTargetScores.keySet().forEach(key -> rankTargetsWithinComponent(key));
}
@@ -826,6 +870,12 @@ public class ChooserListAdapter extends ResolverListAdapter {
return mServiceTargets.get(value).getChooserTarget();
}
+ protected boolean alwaysShowSubLabel() {
+ // Always show a subLabel for visual consistency across list items. Show an empty
+ // subLabel if the subLabel is the same as the label
+ return true;
+ }
+
/**
* Rather than fully sorting the input list, this sorting task will put the top k elements
* in the head of input list and fill the tail with other elements in undetermined order.
@@ -867,6 +917,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
if (getAppPredictor() != null) {
getAppPredictor().unregisterPredictionUpdates(mAppPredictorCallback);
getAppPredictor().destroy();
+ setAppPredictor(null);
}
}
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 774be3c9c4b8832241aa216154b1ec0d1274b1e3..ffa6041721c6f5e34479ca674da65d6f2b24e2e5 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -38,6 +38,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
private final ChooserProfileDescriptor[] mItems;
private final boolean mIsSendAction;
+ private int mBottomOffset;
ChooserMultiProfilePagerAdapter(Context context,
ChooserActivity.ChooserGridAdapter adapter,
@@ -245,6 +246,16 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
}
}
+ void setEmptyStateBottomOffset(int bottomOffset) {
+ mBottomOffset = bottomOffset;
+ }
+
+ @Override
+ protected void setupContainerPadding(View container) {
+ container.setPadding(container.getPaddingLeft(), container.getPaddingTop(),
+ container.getPaddingRight(), container.getPaddingBottom() + mBottomOffset);
+ }
+
class ChooserProfileDescriptor extends ProfileDescriptor {
private ChooserActivity.ChooserGridAdapter chooserGridAdapter;
private RecyclerView recyclerView;
diff --git a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
index 3991a7674f38b7fce82e14d2fb2e1d5a53205da4..21063d5d5857b1e3ffbc02b5cbf8b4c01f618144 100644
--- a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
@@ -26,7 +26,6 @@ import static java.util.stream.Collectors.toList;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.ComponentName;
@@ -34,21 +33,25 @@ import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Pair;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.app.chooser.DisplayResolveInfo;
+import com.android.internal.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Shows a dialog with actions to take on a chooser target.
@@ -68,47 +71,86 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment
mTargetInfos = targets;
}
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
+ /**
+ * Recreate the layout from scratch to match new Sharesheet redlines
+ */
+ public View onCreateView(LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ Bundle savedInstanceState) {
+
+ // Make the background transparent to show dialog rounding
+ Optional.of(getDialog()).map(Dialog::getWindow)
+ .ifPresent(window -> {
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ });
// Fetch UI details from target info
- List> items = mTargetInfos.stream().map(dri -> {
- return new Pair<>(getItemLabel(dri), getItemIcon(dri));
+ List> items = mTargetInfos.stream().map(dri -> {
+ return new Pair<>(getItemIcon(dri), getItemLabel(dri));
}).collect(toList());
+ View v = inflater.inflate(R.layout.chooser_dialog, container, false);
+
+ TextView title = v.findViewById(R.id.title);
+ ImageView icon = v.findViewById(R.id.icon);
+ RecyclerView rv = v.findViewById(R.id.listContainer);
+
final ResolveInfoPresentationGetter pg = getProvidingAppPresentationGetter();
- return new Builder(getContext())
- .setTitle(pg.getLabel())
- .setIcon(pg.getIcon(mUserHandle))
- .setCancelable(true)
- .setAdapter(getAdapterForContent(items), this)
- .create();
+ title.setText(pg.getLabel());
+ icon.setImageDrawable(pg.getIcon(mUserHandle));
+ rv.setAdapter(new VHAdapter(items));
+
+ return v;
+ }
+
+ class VHAdapter extends RecyclerView.Adapter {
+
+ List> mItems;
+
+ VHAdapter(List> items) {
+ mItems = items;
+ }
+
+ @NonNull
+ @Override
+ public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new VH(LayoutInflater.from(parent.getContext()).inflate(
+ R.layout.chooser_dialog_item, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull VH holder, int position) {
+ holder.bind(mItems.get(position), position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mItems.size();
+ }
}
- protected ArrayAdapter> getAdapterForContent(
- List> items) {
- return new ArrayAdapter>(getContext(),
- R.layout.chooser_dialog_item, R.id.text, items) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View v = super.getView(position, convertView, parent); // super recycles views
- TextView label = v.findViewById(R.id.text);
- ImageView icon = v.findViewById(R.id.icon);
-
- Pair pair = getItem(position);
- label.setText(pair.first);
-
- // Hide icon view if one isn't available
- if (pair.second == null) {
- icon.setVisibility(View.GONE);
- } else {
- icon.setImageDrawable(pair.second);
- icon.setVisibility(View.VISIBLE);
- }
-
- return v;
+ class VH extends RecyclerView.ViewHolder {
+ TextView mLabel;
+ ImageView mIcon;
+
+ VH(@NonNull View itemView) {
+ super(itemView);
+ mLabel = itemView.findViewById(R.id.text);
+ mIcon = itemView.findViewById(R.id.icon);
+ }
+
+ public void bind(Pair item, int position) {
+ mLabel.setText(item.second);
+
+ if (item.first == null) {
+ mIcon.setVisibility(View.GONE);
+ } else {
+ mIcon.setVisibility(View.VISIBLE);
+ mIcon.setImageDrawable(item.first);
}
- };
+
+ itemView.setOnClickListener(v -> onClick(getDialog(), position));
+ }
}
@Override
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 06c21ab8832d7d0082d80db2e2c03dfc928da32d..51e56b7fca43ca10d64bc939906a368581bd76db 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -36,10 +36,10 @@ interface IAppOpsService {
// and not be reordered
int checkOperation(int code, int uid, String packageName);
int noteOperation(int code, int uid, String packageName, @nullable String attributionTag,
- boolean shouldCollectAsyncNotedOp, String message);
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
int startOperation(IBinder clientId, int code, int uid, String packageName,
@nullable String attributionTag, boolean startIfModeDefault,
- boolean shouldCollectAsyncNotedOp, String message);
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
@UnsupportedAppUsage
void finishOperation(IBinder clientId, int code, int uid, String packageName,
@nullable String attributionTag);
@@ -54,7 +54,8 @@ interface IAppOpsService {
int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
String proxiedAttributionTag, int proxyUid, String proxyPackageName,
- String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message);
+ String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message,
+ boolean shouldCollectMessage);
// Remaining methods are only used in Java.
int checkPackage(int uid, String packageName);
@@ -76,6 +77,7 @@ interface IAppOpsService {
void addHistoricalOps(in AppOpsManager.HistoricalOps ops);
void resetHistoryParameters();
void clearHistory();
+ void rebootHistory(long offlineDurationMillis);
List getUidOps(int uid, in int[] ops);
void setUidMode(int code, int uid, int mode);
@UnsupportedAppUsage
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 71ee8af8b11a97285cbcb3d2aeb1244ef02c1be8..15ba8e8c11f78bb06b93139552002a5d55746d80 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -265,4 +265,16 @@ interface IVoiceInteractionManagerService {
void performDirectAction(in IBinder token, String actionId, in Bundle arguments, int taskId,
IBinder assistToken, in RemoteCallback cancellationCallback,
in RemoteCallback resultCallback);
+
+ /**
+ * Temporarily disables voice interaction (for example, on Automotive when the display is off).
+ *
+ * It will shutdown the service, and only re-enable it after it's called again (or after a
+ * system restart).
+ *
+ * NOTE: it's only effective when the service itself is available / enabled in the device, so
+ * calling setDisable(false) would be a no-op when it isn't.
+ */
+ void setDisabled(boolean disabled);
+
}
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index e65d1fe9ce53720d0e31c52d71f7eec26265d924..61a52bcc03f9166ee9f157768517ab471a421696 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -18,6 +18,7 @@ package com.android.internal.app;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+import static com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER;
import static com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE;
import android.annotation.Nullable;
@@ -246,6 +247,7 @@ public class IntentForwarderActivity extends Activity {
int selectedProfile = findSelectedProfile(className);
sanitizeIntent(intentReceived);
intentReceived.putExtra(EXTRA_SELECTED_PROFILE, selectedProfile);
+ intentReceived.putExtra(EXTRA_CALLING_USER, UserHandle.of(callingUserId));
startActivityAsCaller(intentReceived, null, null, false, userId);
finish();
}
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 157e0a74712b846ad3ff88af3ad16b3db70f3b4d..986bbc8628ec16d31080f86897aac065a1c6e382 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,29 +17,32 @@
package com.android.internal.app;
import android.animation.ObjectAnimator;
-import android.animation.TimeAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActionBar;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
-import android.graphics.Matrix;
+import android.graphics.LinearGradient;
import android.graphics.Paint;
-import android.graphics.Path;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.provider.Settings;
+import android.util.AttributeSet;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.animation.PathInterpolator;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.internal.R;
@@ -50,23 +53,16 @@ import org.json.JSONObject;
* @hide
*/
public class PlatLogoActivity extends Activity {
- ImageView mZeroView, mOneView;
- BackslashDrawable mBackslash;
- int mClicks;
-
- static final Paint sPaint = new Paint();
- static {
- sPaint.setStyle(Paint.Style.STROKE);
- sPaint.setStrokeWidth(4f);
- sPaint.setStrokeCap(Paint.Cap.SQUARE);
- }
+ private static final boolean WRITE_SETTINGS = true;
+
+ private static final String R_EGG_UNLOCK_SETTING = "egg_mode_r";
+
+ private static final int UNLOCK_TRIES = 3;
+
+ BigDialView mDialView;
@Override
protected void onPause() {
- if (mBackslash != null) {
- mBackslash.stopAnimating();
- }
- mClicks = 0;
super.onPause();
}
@@ -80,114 +76,46 @@ public class PlatLogoActivity extends Activity {
getWindow().setNavigationBarColor(0);
getWindow().setStatusBarColor(0);
- getActionBar().hide();
-
- setContentView(R.layout.platlogo_layout);
-
- mBackslash = new BackslashDrawable((int) (50 * dp));
-
- mOneView = findViewById(R.id.one);
- mOneView.setImageDrawable(new OneDrawable());
- mZeroView = findViewById(R.id.zero);
- mZeroView.setImageDrawable(new ZeroDrawable());
-
- final ViewGroup root = (ViewGroup) mOneView.getParent();
- root.setClipChildren(false);
- root.setBackground(mBackslash);
- root.getBackground().setAlpha(0x20);
-
- View.OnTouchListener tl = new View.OnTouchListener() {
- float mOffsetX, mOffsetY;
- long mClickTime;
- ObjectAnimator mRotAnim;
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- measureTouchPressure(event);
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- v.animate().scaleX(1.1f).scaleY(1.1f);
- v.getParent().bringChildToFront(v);
- mOffsetX = event.getRawX() - v.getX();
- mOffsetY = event.getRawY() - v.getY();
- long now = System.currentTimeMillis();
- if (now - mClickTime < 350) {
- mRotAnim = ObjectAnimator.ofFloat(v, View.ROTATION,
- v.getRotation(), v.getRotation() + 3600);
- mRotAnim.setDuration(10000);
- mRotAnim.start();
- mClickTime = 0;
- } else {
- mClickTime = now;
- }
- break;
- case MotionEvent.ACTION_MOVE:
- v.setX(event.getRawX() - mOffsetX);
- v.setY(event.getRawY() - mOffsetY);
- v.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE);
- break;
- case MotionEvent.ACTION_UP:
- v.performClick();
- // fall through
- case MotionEvent.ACTION_CANCEL:
- v.animate().scaleX(1f).scaleY(1f);
- if (mRotAnim != null) mRotAnim.cancel();
- testOverlap();
- break;
- }
- return true;
- }
- };
-
- findViewById(R.id.one).setOnTouchListener(tl);
- findViewById(R.id.zero).setOnTouchListener(tl);
- findViewById(R.id.text).setOnTouchListener(tl);
- }
+ final ActionBar ab = getActionBar();
+ if (ab != null) ab.hide();
- private void testOverlap() {
- final float width = mZeroView.getWidth();
- final float targetX = mZeroView.getX() + width * .2f;
- final float targetY = mZeroView.getY() + width * .3f;
- if (Math.hypot(targetX - mOneView.getX(), targetY - mOneView.getY()) < width * .2f
- && Math.abs(mOneView.getRotation() % 360 - 315) < 15) {
- mOneView.animate().x(mZeroView.getX() + width * .2f);
- mOneView.animate().y(mZeroView.getY() + width * .3f);
- mOneView.setRotation(mOneView.getRotation() % 360);
- mOneView.animate().rotation(315);
- mOneView.performHapticFeedback(HapticFeedbackConstants.CONFIRM);
-
- mBackslash.startAnimating();
-
- mClicks++;
- if (mClicks >= 7) {
- launchNextStage();
- }
+ mDialView = new BigDialView(this, null);
+ if (Settings.System.getLong(getContentResolver(),
+ R_EGG_UNLOCK_SETTING, 0) == 0) {
+ mDialView.setUnlockTries(UNLOCK_TRIES);
} else {
- mBackslash.stopAnimating();
+ mDialView.setUnlockTries(0);
}
+
+ final FrameLayout layout = new FrameLayout(this);
+ layout.setBackgroundColor(0xFFFF0000);
+ layout.addView(mDialView, FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT);
+ setContentView(layout);
}
- private void launchNextStage() {
+ private void launchNextStage(boolean locked) {
final ContentResolver cr = getContentResolver();
- if (Settings.System.getLong(cr, "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) {
- // For posterity: the moment this user unlocked the easter egg
- try {
+ try {
+ if (WRITE_SETTINGS) {
Settings.System.putLong(cr,
- "egg_mode", // Settings.System.EGG_MODE,
- System.currentTimeMillis());
- } catch (RuntimeException e) {
- Log.e("com.android.internal.app.PlatLogoActivity", "Can't write settings", e);
+ R_EGG_UNLOCK_SETTING,
+ locked ? 0 : System.currentTimeMillis());
}
+ } catch (RuntimeException e) {
+ Log.e("com.android.internal.app.PlatLogoActivity", "Can't write settings", e);
}
+
try {
startActivity(new Intent(Intent.ACTION_MAIN)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK)
.addCategory("com.android.internal.category.PLATLOGO"));
} catch (ActivityNotFoundException ex) {
Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs.");
}
- finish();
+ //finish(); // no longer finish upon unlock; it's fun to frob the dial
}
static final String TOUCH_STATS = "touch.stats";
@@ -223,7 +151,10 @@ public class PlatLogoActivity extends Activity {
if (mPressureMax >= 0) {
touchData.put("min", mPressureMin);
touchData.put("max", mPressureMax);
- Settings.System.putString(getContentResolver(), TOUCH_STATS, touchData.toString());
+ if (WRITE_SETTINGS) {
+ Settings.System.putString(getContentResolver(), TOUCH_STATS,
+ touchData.toString());
+ }
}
} catch (Exception e) {
Log.e("com.android.internal.app.PlatLogoActivity", "Can't write touch settings", e);
@@ -242,149 +173,274 @@ public class PlatLogoActivity extends Activity {
super.onStop();
}
- static class ZeroDrawable extends Drawable {
- int mTintColor;
+ class BigDialView extends ImageView {
+ private static final int COLOR_GREEN = 0xff3ddc84;
+ private static final int COLOR_BLUE = 0xff4285f4;
+ private static final int COLOR_NAVY = 0xff073042;
+ private static final int COLOR_ORANGE = 0xfff86734;
+ private static final int COLOR_CHARTREUSE = 0xffeff7cf;
+ private static final int COLOR_LIGHTBLUE = 0xffd7effe;
- @Override
- public void draw(Canvas canvas) {
- sPaint.setColor(mTintColor | 0xFF000000);
+ private static final int STEPS = 11;
+ private static final float VALUE_CHANGE_MAX = 1f / STEPS;
- canvas.save();
- canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f);
+ private BigDialDrawable mDialDrawable;
+ private boolean mWasLocked;
- canvas.drawCircle(12f, 12f, 10f, sPaint);
- canvas.restore();
+ BigDialView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ init();
}
- @Override
- public void setAlpha(int alpha) { }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) { }
-
- @Override
- public void setTintList(ColorStateList tint) {
- mTintColor = tint.getDefaultColor();
+ BigDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
}
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
+ BigDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
}
- }
- static class OneDrawable extends Drawable {
- int mTintColor;
+ private void init() {
+ mDialDrawable = new BigDialDrawable();
+ setImageDrawable(mDialDrawable);
+ }
@Override
- public void draw(Canvas canvas) {
- sPaint.setColor(mTintColor | 0xFF000000);
-
- canvas.save();
- canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f);
-
- final Path p = new Path();
- p.moveTo(12f, 21.83f);
- p.rLineTo(0f, -19.67f);
- p.rLineTo(-5f, 0f);
- canvas.drawPath(p, sPaint);
- canvas.restore();
+ public void onDraw(Canvas c) {
+ super.onDraw(c);
}
- @Override
- public void setAlpha(int alpha) { }
+ double toPositiveDegrees(double rad) {
+ return (Math.toDegrees(rad) + 360 - 90) % 360;
+ }
@Override
- public void setColorFilter(ColorFilter colorFilter) { }
+ public boolean onTouchEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mWasLocked = mDialDrawable.isLocked();
+ // pass through
+ case MotionEvent.ACTION_MOVE:
+ float x = ev.getX();
+ float y = ev.getY();
+ float cx = (getLeft() + getRight()) / 2f;
+ float cy = (getTop() + getBottom()) / 2f;
+ float angle = (float) toPositiveDegrees(Math.atan2(x - cx, y - cy));
+ final int oldLevel = mDialDrawable.getUserLevel();
+ mDialDrawable.touchAngle(angle);
+ final int newLevel = mDialDrawable.getUserLevel();
+ if (oldLevel != newLevel) {
+ performHapticFeedback(newLevel == STEPS
+ ? HapticFeedbackConstants.CONFIRM
+ : HapticFeedbackConstants.CLOCK_TICK);
+ }
+ return true;
+ case MotionEvent.ACTION_UP:
+ if (mWasLocked != mDialDrawable.isLocked()) {
+ launchNextStage(mDialDrawable.isLocked());
+ }
+ return true;
+ }
+ return false;
+ }
@Override
- public void setTintList(ColorStateList tint) {
- mTintColor = tint.getDefaultColor();
+ public boolean performClick() {
+ if (mDialDrawable.getUserLevel() < STEPS - 1) {
+ mDialDrawable.setUserLevel(mDialDrawable.getUserLevel() + 1);
+ performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+ }
+ return true;
}
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
+ void setUnlockTries(int tries) {
+ mDialDrawable.setUnlockTries(tries);
}
- }
- private static class BackslashDrawable extends Drawable implements TimeAnimator.TimeListener {
- Bitmap mTile;
- Paint mPaint = new Paint();
- BitmapShader mShader;
- TimeAnimator mAnimator = new TimeAnimator();
- Matrix mMatrix = new Matrix();
+ private class BigDialDrawable extends Drawable {
+ public final int STEPS = 10;
+ private int mUnlockTries = 0;
+ final Paint mPaint = new Paint();
+ final Drawable mEleven;
+ private boolean mNightMode;
+ private float mValue = 0f;
+ float mElevenAnim = 0f;
+ ObjectAnimator mElevenShowAnimator = ObjectAnimator.ofFloat(this, "elevenAnim", 0f,
+ 1f).setDuration(300);
+ ObjectAnimator mElevenHideAnimator = ObjectAnimator.ofFloat(this, "elevenAnim", 1f,
+ 0f).setDuration(500);
+
+ BigDialDrawable() {
+ mNightMode = getContext().getResources().getConfiguration().isNightModeActive();
+ mEleven = getContext().getDrawable(R.drawable.ic_number11);
+ mElevenShowAnimator.setInterpolator(new PathInterpolator(0.4f, 0f, 0.2f, 1f));
+ mElevenHideAnimator.setInterpolator(new PathInterpolator(0.8f, 0.2f, 0.6f, 1f));
+ }
- public void draw(Canvas canvas) {
- canvas.drawPaint(mPaint);
- }
+ public void setUnlockTries(int count) {
+ if (mUnlockTries != count) {
+ mUnlockTries = count;
+ setValue(getValue());
+ invalidateSelf();
+ }
+ }
- BackslashDrawable(int width) {
- mTile = Bitmap.createBitmap(width, width, Bitmap.Config.ALPHA_8);
- mAnimator.setTimeListener(this);
-
- final Canvas tileCanvas = new Canvas(mTile);
- final float w = tileCanvas.getWidth();
- final float h = tileCanvas.getHeight();
-
- final Path path = new Path();
- path.moveTo(0, 0);
- path.lineTo(w / 2, 0);
- path.lineTo(w, h / 2);
- path.lineTo(w, h);
- path.close();
-
- path.moveTo(0, h / 2);
- path.lineTo(w / 2, h);
- path.lineTo(0, h);
- path.close();
-
- final Paint slashPaint = new Paint();
- slashPaint.setAntiAlias(true);
- slashPaint.setStyle(Paint.Style.FILL);
- slashPaint.setColor(0xFF000000);
- tileCanvas.drawPath(path, slashPaint);
-
- //mPaint.setColor(0xFF0000FF);
- mShader = new BitmapShader(mTile, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
- mPaint.setShader(mShader);
- }
+ boolean isLocked() {
+ return mUnlockTries > 0;
+ }
- public void startAnimating() {
- if (!mAnimator.isStarted()) {
- mAnimator.start();
+ public void setValue(float v) {
+ // until the dial is "unlocked", you can't turn it all the way to 11
+ final float max = isLocked() ? 1f - 1f / STEPS : 1f;
+ mValue = v < 0f ? 0f : v > max ? max : v;
+ invalidateSelf();
}
- }
- public void stopAnimating() {
- if (mAnimator.isStarted()) {
- mAnimator.cancel();
+ public float getValue() {
+ return mValue;
}
- }
- @Override
- public void setAlpha(int alpha) {
- mPaint.setAlpha(alpha);
- }
+ public int getUserLevel() {
+ return Math.round(getValue() * STEPS - 0.25f);
+ }
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- mPaint.setColorFilter(colorFilter);
- }
+ public void setUserLevel(int i) {
+ setValue(getValue() + ((float) i) / STEPS);
+ }
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
+ public float getElevenAnim() {
+ return mElevenAnim;
+ }
- @Override
- public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
- if (mShader != null) {
- mMatrix.postTranslate(deltaTime / 4f, 0);
- mShader.setLocalMatrix(mMatrix);
- invalidateSelf();
+ public void setElevenAnim(float f) {
+ if (mElevenAnim != f) {
+ mElevenAnim = f;
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ final Rect bounds = getBounds();
+ final int w = bounds.width();
+ final int h = bounds.height();
+ final float w2 = w / 2f;
+ final float h2 = h / 2f;
+ final float radius = w / 4f;
+
+ canvas.drawColor(mNightMode ? COLOR_NAVY : COLOR_LIGHTBLUE);
+
+ canvas.save();
+ canvas.rotate(45, w2, h2);
+ canvas.clipRect(w2, h2 - radius, Math.min(w, h), h2 + radius);
+ final int gradientColor = mNightMode ? 0x60000020 : (0x10FFFFFF & COLOR_NAVY);
+ mPaint.setShader(
+ new LinearGradient(w2, h2, Math.min(w, h), h2, gradientColor,
+ 0x00FFFFFF & gradientColor, Shader.TileMode.CLAMP));
+ mPaint.setColor(Color.BLACK);
+ canvas.drawPaint(mPaint);
+ mPaint.setShader(null);
+ canvas.restore();
+
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(COLOR_GREEN);
+
+ canvas.drawCircle(w2, h2, radius, mPaint);
+
+ mPaint.setColor(mNightMode ? COLOR_LIGHTBLUE : COLOR_NAVY);
+ final float cx = w * 0.85f;
+ for (int i = 0; i < STEPS; i++) {
+ final float f = (float) i / STEPS;
+ canvas.save();
+ final float angle = valueToAngle(f);
+ canvas.rotate(-angle, w2, h2);
+ canvas.drawCircle(cx, h2, (i <= getUserLevel()) ? 20 : 5, mPaint);
+ canvas.restore();
+ }
+
+ if (mElevenAnim > 0f) {
+ final int color = COLOR_ORANGE;
+ final int size2 = (int) ((0.5 + 0.5f * mElevenAnim) * w / 14);
+ final float cx11 = cx + size2 / 4f;
+ mEleven.setBounds((int) cx11 - size2, (int) h2 - size2,
+ (int) cx11 + size2, (int) h2 + size2);
+ final int alpha = 0xFFFFFF | ((int) clamp(0xFF * 2 * mElevenAnim, 0, 0xFF)
+ << 24);
+ mEleven.setTint(alpha & color);
+ mEleven.draw(canvas);
+ }
+
+ // don't want to use the rounded value here since the quantization will be visible
+ final float angle = valueToAngle(mValue);
+
+ // it's easier to draw at far-right and rotate backwards
+ canvas.rotate(-angle, w2, h2);
+ mPaint.setColor(Color.WHITE);
+ final float dimple = w2 / 12f;
+ canvas.drawCircle(w - radius - dimple * 2, h2, dimple, mPaint);
+ }
+
+ float clamp(float x, float a, float b) {
+ return x < a ? a : x > b ? b : x;
+ }
+
+ float angleToValue(float a) {
+ return 1f - clamp(a / (360 - 45), 0f, 1f);
+ }
+
+ // rotation: min is at 4:30, max is at 3:00
+ float valueToAngle(float v) {
+ return (1f - v) * (360 - 45);
+ }
+
+ public void touchAngle(float a) {
+ final int oldUserLevel = getUserLevel();
+ final float newValue = angleToValue(a);
+ // this is how we prevent the knob from snapping from max back to min, or from
+ // jumping around wherever the user presses. The new value must be pretty close
+ // to the
+ // previous one.
+ if (Math.abs(newValue - getValue()) < VALUE_CHANGE_MAX) {
+ setValue(newValue);
+
+ if (isLocked() && oldUserLevel != STEPS - 1 && getUserLevel() == STEPS - 1) {
+ mUnlockTries--;
+ } else if (!isLocked() && getUserLevel() == 0) {
+ mUnlockTries = UNLOCK_TRIES;
+ }
+
+ if (!isLocked()) {
+ if (getUserLevel() == STEPS && mElevenAnim != 1f
+ && !mElevenShowAnimator.isRunning()) {
+ mElevenHideAnimator.cancel();
+ mElevenShowAnimator.start();
+ } else if (getUserLevel() != STEPS && mElevenAnim == 1f
+ && !mElevenHideAnimator.isRunning()) {
+ mElevenShowAnimator.cancel();
+ mElevenHideAnimator.start();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setAlpha(int i) {
+ }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
}
}
}
}
+
+
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index b82f0dfcbe12dc27f94e8579d1601c483dad98d8..233231cfcfdf0c3ceb82a1c656243d7f09012055 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -20,9 +20,6 @@ import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.PermissionChecker.PID_UNKNOWN;
-import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
-import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_WORK;
-
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UiThread;
@@ -159,9 +156,9 @@ public class ResolverActivity extends Activity implements
protected static final String METRICS_CATEGORY_RESOLVER = "intent_resolver";
protected static final String METRICS_CATEGORY_CHOOSER = "intent_chooser";
- /**
- * TODO(arangelov): Remove a couple of weeks after work/personal tabs are finalized.
- */
+ /** Tracks if we should ignore future broadcasts telling us the work profile is enabled */
+ private boolean mWorkProfileHasBeenEnabled = false;
+
@VisibleForTesting
public static boolean ENABLE_TABBED_VIEW = true;
private static final String TAB_TAG_PERSONAL = "personal";
@@ -184,6 +181,18 @@ public class ResolverActivity extends Activity implements
static final String EXTRA_SELECTED_PROFILE =
"com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE";
+ /**
+ * {@link UserHandle} extra to indicate the user of the user that the starting intent
+ * originated from.
+ *
This is not necessarily the same as {@link #getUserId()} or {@link UserHandle#myUserId()},
+ * as there are edge cases when the intent resolver is launched in the other profile.
+ * For example, when we have 0 resolved apps in current profile and multiple resolved
+ * apps in the other profile, opening a link from the current profile launches the intent
+ * resolver in the other one. b/148536209 for more info.
+ */
+ static final String EXTRA_CALLING_USER =
+ "com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER";
+
static final int PROFILE_PERSONAL = AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
static final int PROFILE_WORK = AbstractMultiProfilePagerAdapter.PROFILE_WORK;
@@ -470,17 +479,20 @@ public class ResolverActivity extends Activity implements
// the intent resolver is started in the other profile. Since this is the only case when
// this happens, we check for it here and set the current profile's tab.
int selectedProfile = getCurrentProfile();
- UserHandle intentUser = UserHandle.of(getLaunchingUserId());
+ UserHandle intentUser = getIntent().hasExtra(EXTRA_CALLING_USER)
+ ? getIntent().getParcelableExtra(EXTRA_CALLING_USER)
+ : getUser();
if (!getUser().equals(intentUser)) {
if (getPersonalProfileUserHandle().equals(intentUser)) {
selectedProfile = PROFILE_PERSONAL;
} else if (getWorkProfileUserHandle().equals(intentUser)) {
selectedProfile = PROFILE_WORK;
}
- }
- int selectedProfileExtra = getSelectedProfileExtra();
- if (selectedProfileExtra != -1) {
- selectedProfile = selectedProfileExtra;
+ } else {
+ int selectedProfileExtra = getSelectedProfileExtra();
+ if (selectedProfileExtra != -1) {
+ selectedProfile = selectedProfileExtra;
+ }
}
// We only show the default app for the profile of the current user. The filterLastUsed
// flag determines whether to show a default app and that app is not shown in the
@@ -535,22 +547,6 @@ public class ResolverActivity extends Activity implements
return selectedProfile;
}
- /**
- * Returns the user id of the user that the starting intent originated from.
- *
This is not necessarily equal to {@link #getUserId()} or {@link UserHandle#myUserId()},
- * as there are edge cases when the intent resolver is launched in the other profile.
- * For example, when we have 0 resolved apps in current profile and multiple resolved apps
- * in the other profile, opening a link from the current profile launches the intent resolver
- * in the other one. b/148536209 for more info.
- */
- private int getLaunchingUserId() {
- int contentUserHint = getIntent().getContentUserHint();
- if (contentUserHint == UserHandle.USER_CURRENT) {
- return UserHandle.myUserId();
- }
- return contentUserHint;
- }
-
protected @Profile int getCurrentProfile() {
return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK);
}
@@ -829,12 +825,23 @@ public class ResolverActivity extends Activity implements
if (shouldShowTabs()) {
mWorkProfileStateReceiver = createWorkProfileStateReceiver();
registerWorkProfileStateReceiver();
+
+ mWorkProfileHasBeenEnabled = isWorkProfileEnabled();
}
}
+ private boolean isWorkProfileEnabled() {
+ UserHandle workUserHandle = getWorkProfileUserHandle();
+ UserManager userManager = getSystemService(UserManager.class);
+
+ return !userManager.isQuietModeEnabled(workUserHandle)
+ && userManager.isUserUnlocked(workUserHandle);
+ }
+
private void registerWorkProfileStateReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
registerReceiverAsUser(mWorkProfileStateReceiver, UserHandle.ALL, filter, null, null);
}
@@ -1235,8 +1242,8 @@ public class ResolverActivity extends Activity implements
}
if (target != null) {
- if (intent != null) {
- intent.fixUris(UserHandle.myUserId());
+ if (intent != null && isLaunchingTargetInOtherProfile()) {
+ prepareIntentForCrossProfileLaunch(intent);
}
safelyStartActivity(target);
@@ -1250,6 +1257,15 @@ public class ResolverActivity extends Activity implements
return true;
}
+ private void prepareIntentForCrossProfileLaunch(Intent intent) {
+ intent.fixUris(UserHandle.myUserId());
+ }
+
+ private boolean isLaunchingTargetInOtherProfile() {
+ return mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
+ != UserHandle.myUserId();
+ }
+
@VisibleForTesting
public void safelyStartActivity(TargetInfo cti) {
// We're dispatching intents that might be coming from legacy apps, so
@@ -1263,13 +1279,17 @@ public class ResolverActivity extends Activity implements
}
private void safelyStartActivityInternal(TargetInfo cti) {
- if (mPersonalPackageMonitor != null) {
- mPersonalPackageMonitor.unregister();
- }
- if (mWorkPackageMonitor != null) {
- mWorkPackageMonitor.unregister();
+ // If the target is suspended, the activity will not be successfully launched.
+ // Do not unregister from package manager updates in this case
+ if (!cti.isSuspended()) {
+ if (mPersonalPackageMonitor != null) {
+ mPersonalPackageMonitor.unregister();
+ }
+ if (mWorkPackageMonitor != null) {
+ mWorkPackageMonitor.unregister();
+ }
+ mRegistered = false;
}
- mRegistered = false;
// If needed, show that intent is forwarded
// from managed profile to owner or other way around.
if (mProfileSwitchMessageId != -1) {
@@ -1646,10 +1666,18 @@ public class ResolverActivity extends Activity implements
viewPager.setVisibility(View.VISIBLE);
tabHost.setCurrentTab(mMultiProfilePagerAdapter.getCurrentPage());
mMultiProfilePagerAdapter.setOnProfileSelectedListener(
- index -> {
- tabHost.setCurrentTab(index);
- resetButtonBar();
- resetCheckedItem();
+ new AbstractMultiProfilePagerAdapter.OnProfileSelectedListener() {
+ @Override
+ public void onProfileSelected(int index) {
+ tabHost.setCurrentTab(index);
+ resetButtonBar();
+ resetCheckedItem();
+ }
+
+ @Override
+ public void onProfilePageStateChanged(int state) {
+ onHorizontalSwipeStateChanged(state);
+ }
});
mMultiProfilePagerAdapter.setOnSwitchOnWorkSelectedListener(
() -> {
@@ -1661,6 +1689,8 @@ public class ResolverActivity extends Activity implements
findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE);
}
+ void onHorizontalSwipeStateChanged(int state) {}
+
private void maybeHideDivider() {
if (!isIntentPicker()) {
return;
@@ -1801,6 +1831,12 @@ public class ResolverActivity extends Activity implements
ResolverListAdapter activeListAdapter =
mMultiProfilePagerAdapter.getActiveListAdapter();
View buttonBarDivider = findViewById(R.id.resolver_button_bar_divider);
+ if (!useLayoutWithDefault()) {
+ int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
+ buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
+ buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
+ R.dimen.resolver_button_bar_spacing) + inset);
+ }
if (activeListAdapter.isTabLoaded()
&& mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)
&& !useLayoutWithDefault()) {
@@ -1817,12 +1853,6 @@ public class ResolverActivity extends Activity implements
buttonLayout.setVisibility(View.VISIBLE);
setButtonBarIgnoreOffset(/* ignoreOffset */ true);
- if (!useLayoutWithDefault()) {
- int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
- buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
- buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
- R.dimen.resolver_button_bar_spacing) + inset);
- }
mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
@@ -1923,7 +1953,7 @@ public class ResolverActivity extends Activity implements
ResolverListAdapter activeListAdapter =
mMultiProfilePagerAdapter.getActiveListAdapter();
activeListAdapter.notifyDataSetChanged();
- if (activeListAdapter.getCount() == 0) {
+ if (activeListAdapter.getCount() == 0 && !inactiveListAdapterHasItems()) {
// We no longer have any items... just finish the activity.
finish();
}
@@ -1933,23 +1963,42 @@ public class ResolverActivity extends Activity implements
}
}
+ private boolean inactiveListAdapterHasItems() {
+ if (!shouldShowTabs()) {
+ return false;
+ }
+ return mMultiProfilePagerAdapter.getInactiveListAdapter().getCount() > 0;
+ }
+
private BroadcastReceiver createWorkProfileStateReceiver() {
return new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (!TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)
- && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
+ && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+ && !TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_AVAILABLE)) {
return;
}
- int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)
- && userHandle != getWorkProfileUserHandle().getIdentifier()) {
+
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+
+ if (userId != getWorkProfileUserHandle().getIdentifier()) {
return;
}
- if (TextUtils.equals(action, Intent.ACTION_USER_UNLOCKED)) {
+
+ if (isWorkProfileEnabled()) {
+ if (mWorkProfileHasBeenEnabled) {
+ return;
+ }
+
+ mWorkProfileHasBeenEnabled = true;
mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
+ } else {
+ // Must be an UNAVAILABLE broadcast, so we watch for the next availability
+ mWorkProfileHasBeenEnabled = false;
}
+
if (mMultiProfilePagerAdapter.getCurrentUserHandle()
.equals(getWorkProfileUserHandle())) {
mMultiProfilePagerAdapter.rebuildActiveTab(true);
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 97f43d27a9ce1a53410070b350ea393f065ae3bd..eef722e32bdcde7bec2a4c74f91a4684a407762d 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -54,7 +54,6 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
-import com.android.internal.app.chooser.SelectableTargetInfo;
import com.android.internal.app.chooser.TargetInfo;
import java.util.ArrayList;
@@ -68,7 +67,7 @@ public class ResolverListAdapter extends BaseAdapter {
private final List mBaseResolveList;
private final PackageManager mPm;
protected final Context mContext;
- private final ColorMatrixColorFilter mSuspendedMatrixColorFilter;
+ private static ColorMatrixColorFilter sSuspendedMatrixColorFilter;
private final int mIconDpi;
protected ResolveInfo mLastChosen;
private DisplayResolveInfo mOtherProfile;
@@ -103,7 +102,6 @@ public class ResolverListAdapter extends BaseAdapter {
mDisplayList = new ArrayList<>();
mFilterLastUsed = filterLastUsed;
mResolverListController = resolverListController;
- mSuspendedMatrixColorFilter = createSuspendedColorMatrix();
mResolverListCommunicator = resolverListCommunicator;
mIsAudioCaptureDevice = isAudioCaptureDevice;
final ActivityManager am = (ActivityManager) mContext.getSystemService(ACTIVITY_SERVICE);
@@ -423,11 +421,12 @@ public class ResolverListAdapter extends BaseAdapter {
// We assume that at this point we've already filtered out the only intent for a different
// targetUserId which we're going to use.
private void addResolveInfo(DisplayResolveInfo dri) {
- // TODO(arangelov): Is that UserHandle.USER_CURRENT check okay?
if (dri != null && dri.getResolveInfo() != null
&& dri.getResolveInfo().targetUserId == UserHandle.USER_CURRENT) {
if (shouldAddResolveInfo(dri)) {
mDisplayList.add(dri);
+ Log.i(TAG, "Add DisplayResolveInfo component: " + dri.getResolvedComponentName()
+ + ", intent component: " + dri.getResolvedIntent().getComponent());
}
}
}
@@ -539,29 +538,14 @@ public class ResolverListAdapter extends BaseAdapter {
&& !((DisplayResolveInfo) info).hasDisplayLabel()) {
getLoadLabelTask((DisplayResolveInfo) info, holder).execute();
} else {
- holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo());
- if (info instanceof SelectableTargetInfo) {
- // direct share targets should append the application name for a better readout
- DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo();
- CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
- CharSequence extendedInfo = info.getExtendedInfo();
- String contentDescription = String.join(" ", info.getDisplayLabel(),
- extendedInfo != null ? extendedInfo : "", appName);
- holder.updateContentDescription(contentDescription);
- }
- }
-
- if (info.isSuspended()) {
- holder.icon.setColorFilter(mSuspendedMatrixColorFilter);
- } else {
- holder.icon.setColorFilter(null);
+ holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel());
}
if (info instanceof DisplayResolveInfo
&& !((DisplayResolveInfo) info).hasDisplayIcon()) {
- new ResolverListAdapter.LoadIconTask((DisplayResolveInfo) info, holder.icon).execute();
+ new LoadIconTask((DisplayResolveInfo) info, holder).execute();
} else {
- holder.icon.setImageDrawable(info.getDisplayIcon(mContext));
+ holder.bindIcon(info);
}
}
@@ -579,23 +563,27 @@ public class ResolverListAdapter extends BaseAdapter {
}
}
- private ColorMatrixColorFilter createSuspendedColorMatrix() {
- int grayValue = 127;
- float scale = 0.5f; // half bright
+ private static ColorMatrixColorFilter getSuspendedColorMatrix() {
+ if (sSuspendedMatrixColorFilter == null) {
+
+ int grayValue = 127;
+ float scale = 0.5f; // half bright
- ColorMatrix tempBrightnessMatrix = new ColorMatrix();
- float[] mat = tempBrightnessMatrix.getArray();
- mat[0] = scale;
- mat[6] = scale;
- mat[12] = scale;
- mat[4] = grayValue;
- mat[9] = grayValue;
- mat[14] = grayValue;
+ ColorMatrix tempBrightnessMatrix = new ColorMatrix();
+ float[] mat = tempBrightnessMatrix.getArray();
+ mat[0] = scale;
+ mat[6] = scale;
+ mat[12] = scale;
+ mat[4] = grayValue;
+ mat[9] = grayValue;
+ mat[14] = grayValue;
- ColorMatrix matrix = new ColorMatrix();
- matrix.setSaturation(0.0f);
- matrix.preConcat(tempBrightnessMatrix);
- return new ColorMatrixColorFilter(matrix);
+ ColorMatrix matrix = new ColorMatrix();
+ matrix.setSaturation(0.0f);
+ matrix.preConcat(tempBrightnessMatrix);
+ sSuspendedMatrixColorFilter = new ColorMatrixColorFilter(matrix);
+ }
+ return sSuspendedMatrixColorFilter;
}
ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo ai) {
@@ -614,11 +602,22 @@ public class ResolverListAdapter extends BaseAdapter {
void loadFilteredItemIconTaskAsync(@NonNull ImageView iconView) {
final DisplayResolveInfo iconInfo = getFilteredItem();
if (iconView != null && iconInfo != null) {
- new LoadIconTask(iconInfo, iconView).execute();
+ new AsyncTask() {
+ @Override
+ protected Drawable doInBackground(Void... params) {
+ return loadIconForResolveInfo(iconInfo.getResolveInfo());
+ }
+
+ @Override
+ protected void onPostExecute(Drawable d) {
+ iconView.setImageDrawable(d);
+ }
+ }.execute();
}
}
- UserHandle getUserHandle() {
+ @VisibleForTesting
+ public UserHandle getUserHandle() {
return mResolverListController.getUserHandle();
}
@@ -640,6 +639,10 @@ public class ResolverListAdapter extends BaseAdapter {
mIsTabLoaded = true;
}
+ protected boolean alwaysShowSubLabel() {
+ return false;
+ }
+
/**
* Necessary methods to communicate between {@link ResolverListAdapter}
* and {@link ResolverActivity}.
@@ -682,20 +685,18 @@ public class ResolverListAdapter extends BaseAdapter {
icon = (ImageView) view.findViewById(R.id.icon);
}
- public void bindLabel(CharSequence label, CharSequence subLabel) {
- if (!TextUtils.equals(text.getText(), label)) {
- text.setText(label);
- }
+ public void bindLabel(CharSequence label, CharSequence subLabel, boolean showSubLabel) {
+ text.setText(label);
- // Always show a subLabel for visual consistency across list items. Show an empty
- // subLabel if the subLabel is the same as the label
if (TextUtils.equals(label, subLabel)) {
subLabel = null;
}
- if (!TextUtils.equals(text2.getText(), subLabel)) {
+ text2.setText(subLabel);
+ if (showSubLabel || subLabel != null) {
text2.setVisibility(View.VISIBLE);
- text2.setText(subLabel);
+ } else {
+ text2.setVisibility(View.GONE);
}
itemView.setContentDescription(null);
@@ -704,6 +705,15 @@ public class ResolverListAdapter extends BaseAdapter {
public void updateContentDescription(String description) {
itemView.setContentDescription(description);
}
+
+ public void bindIcon(TargetInfo info) {
+ icon.setImageDrawable(info.getDisplayIcon(itemView.getContext()));
+ if (info.isSuspended()) {
+ icon.setColorFilter(getSuspendedColorMatrix());
+ } else {
+ icon.setColorFilter(null);
+ }
+ }
}
protected class LoadLabelTask extends AsyncTask {
@@ -752,19 +762,19 @@ public class ResolverListAdapter extends BaseAdapter {
protected void onPostExecute(CharSequence[] result) {
mDisplayResolveInfo.setDisplayLabel(result[0]);
mDisplayResolveInfo.setExtendedInfo(result[1]);
- mHolder.bindLabel(result[0], result[1]);
+ mHolder.bindLabel(result[0], result[1], alwaysShowSubLabel());
}
}
class LoadIconTask extends AsyncTask {
- protected final com.android.internal.app.chooser.DisplayResolveInfo mDisplayResolveInfo;
+ protected final DisplayResolveInfo mDisplayResolveInfo;
private final ResolveInfo mResolveInfo;
- private final ImageView mTargetView;
+ private ViewHolder mHolder;
- LoadIconTask(DisplayResolveInfo dri, ImageView target) {
+ LoadIconTask(DisplayResolveInfo dri, ViewHolder holder) {
mDisplayResolveInfo = dri;
mResolveInfo = dri.getResolveInfo();
- mTargetView = target;
+ mHolder = holder;
}
@Override
@@ -778,9 +788,14 @@ public class ResolverListAdapter extends BaseAdapter {
mResolverListCommunicator.updateProfileViewButton();
} else {
mDisplayResolveInfo.setDisplayIcon(d);
- mTargetView.setImageDrawable(d);
+ mHolder.bindIcon(mDisplayResolveInfo);
}
}
+
+ public void setViewHolder(ViewHolder holder) {
+ mHolder = holder;
+ mHolder.bindIcon(mDisplayResolveInfo);
+ }
}
/**
diff --git a/core/java/com/android/internal/app/ResolverViewPager.java b/core/java/com/android/internal/app/ResolverViewPager.java
index 4eb6e3bd2071c5d1b01d462345949fa6f595a1e3..478cc18f13ee24fda28cf05d9048e34bc681c8c4 100644
--- a/core/java/com/android/internal/app/ResolverViewPager.java
+++ b/core/java/com/android/internal/app/ResolverViewPager.java
@@ -18,6 +18,7 @@ package com.android.internal.app;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import com.android.internal.widget.ViewPager;
@@ -30,6 +31,8 @@ import com.android.internal.widget.ViewPager;
*/
public class ResolverViewPager extends ViewPager {
+ private boolean mSwipingEnabled = true;
+
public ResolverViewPager(Context context) {
super(context);
}
@@ -70,4 +73,17 @@ public class ResolverViewPager extends ViewPager {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+
+ /**
+ * Sets whether swiping sideways should happen.
+ *
Note that swiping is always disabled for RTL layouts (b/159110029 for context).
+ */
+ void setSwipingEnabled(boolean swipingEnabled) {
+ mSwipingEnabled = swipingEnabled;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return !isLayoutRtl() && mSwipingEnabled && super.onInterceptTouchEvent(ev);
+ }
}
diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java
index ffe2dbe4ccc065562ad2219dbac8ddd9b4b26973..2d91e64b2e6765c9d7a82a5f5b63fee37261c7dd 100644
--- a/core/java/com/android/internal/app/SimpleIconFactory.java
+++ b/core/java/com/android/internal/app/SimpleIconFactory.java
@@ -19,6 +19,7 @@ package com.android.internal.app;
import static android.content.Context.ACTIVITY_SERVICE;
import static android.graphics.Paint.DITHER_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFraction;
import android.annotation.AttrRes;
import android.annotation.NonNull;
@@ -54,6 +55,7 @@ import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import java.nio.ByteBuffer;
+import java.util.Optional;
/**
@@ -64,6 +66,7 @@ import java.nio.ByteBuffer;
@Deprecated
public class SimpleIconFactory {
+
private static final SynchronizedPool sPool =
new SynchronizedPool<>(Runtime.getRuntime().availableProcessors());
@@ -251,7 +254,7 @@ public class SimpleIconFactory {
} else if (w > h && h > 0) {
scale = (float) w / h;
}
- Bitmap bitmap = createIconBitmap(icon, scale);
+ Bitmap bitmap = createIconBitmapNoInsetOrMask(icon, scale);
bitmap = maskBitmapToCircle(bitmap);
icon = new BitmapDrawable(mContext.getResources(), bitmap);
@@ -281,15 +284,19 @@ public class SimpleIconFactory {
final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
- final Paint paint = new Paint();
- paint.setAntiAlias(true);
+ final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
+ | Paint.FILTER_BITMAP_FLAG);
+
+ // Apply an offset to enable shadow to be drawn
+ final int size = bitmap.getWidth();
+ int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size), 1);
// Draw mask
paint.setColor(0xffffffff);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(bitmap.getWidth() / 2f,
bitmap.getHeight() / 2f,
- bitmap.getWidth() / 2f - 1 /* -1 to avoid circles with flat sides */,
+ bitmap.getWidth() / 2f - offset,
paint);
// Draw masked bitmap
@@ -306,24 +313,61 @@ public class SimpleIconFactory {
}
private Bitmap createIconBitmap(Drawable icon, float scale) {
- return createIconBitmap(icon, scale, mIconBitmapSize);
+ return createIconBitmap(icon, scale, mIconBitmapSize, true, false);
+ }
+
+ private Bitmap createIconBitmapNoInsetOrMask(Drawable icon, float scale) {
+ return createIconBitmap(icon, scale, mIconBitmapSize, false, true);
}
/**
* @param icon drawable that should be flattened to a bitmap
* @param scale the scale to apply before drawing {@param icon} on the canvas
+ * @param insetAdiForShadow when rendering AdaptiveIconDrawables inset to make room for a shadow
+ * @param ignoreAdiMask when rendering AdaptiveIconDrawables ignore the current system mask
*/
- private Bitmap createIconBitmap(Drawable icon, float scale, int size) {
+ private Bitmap createIconBitmap(Drawable icon, float scale, int size, boolean insetAdiForShadow,
+ boolean ignoreAdiMask) {
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
mCanvas.setBitmap(bitmap);
mOldBounds.set(icon.getBounds());
if (icon instanceof AdaptiveIconDrawable) {
- int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size),
- Math.round(size * (1 - scale) / 2));
- icon.setBounds(offset, offset, size - offset, size - offset);
- icon.draw(mCanvas);
+ final AdaptiveIconDrawable adi = (AdaptiveIconDrawable) icon;
+
+ // By default assumes the output bitmap will have a shadow directly applied and makes
+ // room for it by insetting. If there are intermediate steps before applying the shadow
+ // insetting is disableable.
+ int offset = Math.round(size * (1 - scale) / 2);
+ if (insetAdiForShadow) {
+ offset = Math.max((int) Math.ceil(BLUR_FACTOR * size), offset);
+ }
+ Rect bounds = new Rect(offset, offset, size - offset, size - offset);
+
+ // AdaptiveIconDrawables are by default masked by the user's icon shape selection.
+ // If further masking is to be done, directly render to avoid the system masking.
+ if (ignoreAdiMask) {
+ final int cX = bounds.width() / 2;
+ final int cY = bounds.height() / 2;
+ final float portScale = 1f / (1 + 2 * getExtraInsetFraction());
+ final int insetWidth = (int) (bounds.width() / (portScale * 2));
+ final int insetHeight = (int) (bounds.height() / (portScale * 2));
+
+ Rect childRect = new Rect(cX - insetWidth, cY - insetHeight, cX + insetWidth,
+ cY + insetHeight);
+ Optional.ofNullable(adi.getBackground()).ifPresent(drawable -> {
+ drawable.setBounds(childRect);
+ drawable.draw(mCanvas);
+ });
+ Optional.ofNullable(adi.getForeground()).ifPresent(drawable -> {
+ drawable.setBounds(childRect);
+ drawable.draw(mCanvas);
+ });
+ } else {
+ adi.setBounds(bounds);
+ adi.draw(mCanvas);
+ }
} else {
if (icon instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 0589baa76b8a130f2bf883edf1b949e5ce8f52eb..d8eaeda2b5491a8ad2ebdafc143837734d854045 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -26,6 +26,7 @@ import android.Manifest;
import android.annotation.Nullable;
import android.app.AlertDialog;
import android.app.AppGlobals;
+import android.app.KeyguardManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
@@ -208,9 +209,32 @@ public class SuspendedAppActivity extends AlertActivity
ap.mPositiveButtonText = getString(android.R.string.ok);
ap.mNeutralButtonText = resolveNeutralButtonText();
ap.mPositiveButtonListener = ap.mNeutralButtonListener = this;
+
+ requestDismissKeyguardIfNeeded(ap.mMessage);
+
setupAlert();
}
+ private void requestDismissKeyguardIfNeeded(CharSequence dismissMessage) {
+ final KeyguardManager km = getSystemService(KeyguardManager.class);
+ if (km.isKeyguardLocked()) {
+ km.requestDismissKeyguard(this, dismissMessage,
+ new KeyguardManager.KeyguardDismissCallback() {
+ @Override
+ public void onDismissError() {
+ Slog.e(TAG, "Error while dismissing keyguard."
+ + " Keeping the dialog visible.");
+ }
+
+ @Override
+ public void onDismissCancelled() {
+ Slog.w(TAG, "Keyguard dismiss was cancelled. Finishing.");
+ SuspendedAppActivity.this.finish();
+ }
+ });
+ }
+ }
+
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 6f7695ce8c34ae112dd635c31fe0c7769f471b6f..ca0856238b90d1197868754e4390d1388924df13 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -26,6 +26,8 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -97,7 +99,10 @@ public class UnlaunchableAppActivity extends Activity
@Override
public void onClick(DialogInterface dialog, int which) {
if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE && which == DialogInterface.BUTTON_POSITIVE) {
- UserManager.get(this).requestQuietModeEnabled(false, UserHandle.of(mUserId), mTarget);
+ UserManager userManager = UserManager.get(this);
+ new Handler(Looper.getMainLooper()).post(
+ () -> userManager.requestQuietModeEnabled(
+ /* enableQuietMode= */ false, UserHandle.of(mUserId), mTarget));
}
}
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 86a9af3db1964324f7189300e5443b53c503e920..fe0e7d01226267e88260a4d3ed78a026a3119720 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -40,8 +40,10 @@ import java.util.List;
* resolve it to an activity.
*/
public class DisplayResolveInfo implements TargetInfo {
- // Temporary flag for new chooser delegate behavior.
- private static final boolean ENABLE_CHOOSER_DELEGATE = true;
+ // Temporary flag for new chooser delegate behavior. There are occassional token
+ // permission errors from bouncing through the delegate. Watch out before reenabling:
+ // b/157272342 is one example but this issue has been reported many times
+ private static final boolean ENABLE_CHOOSER_DELEGATE = false;
private final ResolveInfo mResolveInfo;
private CharSequence mDisplayLabel;
diff --git a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
index e582583521061953b2bbb4e510b8b39c1ced17cc..cf921d734d48be1c1574d4923e86111aac84f91c 100644
--- a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
@@ -69,6 +69,13 @@ public class MultiDisplayResolveInfo extends DisplayResolveInfo {
mSelected = selected;
}
+ /**
+ * Return selected target.
+ */
+ public DisplayResolveInfo getSelectedTarget() {
+ return hasSelected() ? mTargetInfos.get(mSelected) : null;
+ }
+
/**
* Whether or not the user has selected a specific target for this MultiInfo.
*/
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index b8142607ebd72eb1895136dab04d6c235666bfda..ab58fc0eeafc1ef8d68d3c0bb35139c7a7f45a27 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -49,12 +49,14 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.service.procstats.ProcessStatsProto;
import android.service.procstats.ProcessStatsStateProto;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -1420,10 +1422,38 @@ public final class ProcessState {
proto.end(token);
}
+ /**
+ * Assume the atom already includes a UID field, write the process name only if
+ * it's different from the package name; and only write the suffix if possible.
+ */
+ static void writeCompressedProcessName(final ProtoOutputStream proto, final long fieldId,
+ final String procName, final String packageName, final boolean sharedUid) {
+ if (sharedUid) {
+ // This UID has multiple packages running, write the full process name here
+ proto.write(fieldId, procName);
+ return;
+ }
+ if (TextUtils.equals(procName, packageName)) {
+ // Same name, don't bother to write the process name here.
+ return;
+ }
+ if (procName.startsWith(packageName)) {
+ final int pkgLength = packageName.length();
+ if (procName.charAt(pkgLength) == ':') {
+ // Only write the suffix starting with ':'
+ proto.write(fieldId, procName.substring(pkgLength));
+ return;
+ }
+ }
+ // Write the full process name
+ proto.write(fieldId, procName);
+ }
+
/** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */
public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto, long fieldId,
String procName, int uid, long now,
- final ProcessMap> procToPkgMap) {
+ final ProcessMap> procToPkgMap,
+ final SparseArray> uidToPkgMap) {
// Group proc stats by aggregated type (only screen state + process state)
SparseLongArray durationByState = new SparseLongArray();
boolean didCurState = false;
@@ -1503,7 +1533,8 @@ public final class ProcessState {
// build the output
final long token = proto.start(fieldId);
- proto.write(ProcessStatsProto.PROCESS, procName);
+ writeCompressedProcessName(proto, ProcessStatsProto.PROCESS, procName, mPackage,
+ mMultiPackage || (uidToPkgMap.get(mUid).size() > 1));
proto.write(ProcessStatsProto.UID, uid);
for (int i = 0; i < durationByState.size(); i++) {
@@ -1528,7 +1559,7 @@ public final class ProcessState {
}
mStats.dumpFilteredAssociationStatesProtoForProc(proto, ProcessStatsProto.ASSOCS,
- now, this, procToPkgMap);
+ now, this, procToPkgMap, uidToPkgMap);
proto.end(token);
}
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 928ba35595d387b190b1f269b09dac023ca28930..11e55b852516a40192bd6668ae99cdcbf3bbb80f 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -2232,22 +2232,43 @@ public final class ProcessStats implements Parcelable {
}
/** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */
- public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto) {
- dumpProtoPreamble(proto);
+ public void dumpAggregatedProtoForStatsd(ProtoOutputStream[] protoStreams,
+ long maxRawShardSizeBytes) {
+ int shardIndex = 0;
+ dumpProtoPreamble(protoStreams[shardIndex]);
+
final ArrayMap> procMap = mProcesses.getMap();
- final ProcessMap> procToPkgMap =
- collectProcessPackageMaps(null, false);
+ final ProcessMap> procToPkgMap = new ProcessMap<>();
+ final SparseArray> uidToPkgMap = new SparseArray<>();
+ collectProcessPackageMaps(null, false, procToPkgMap, uidToPkgMap);
+
for (int ip = 0; ip < procMap.size(); ip++) {
final String procName = procMap.keyAt(ip);
+ if (protoStreams[shardIndex].getRawSize() > maxRawShardSizeBytes) {
+ shardIndex++;
+ if (shardIndex >= protoStreams.length) {
+ // We have run out of space; we'll drop the rest of the processes.
+ Slog.d(TAG, String.format("Dropping process indices from %d to %d from "
+ + "statsd proto (too large)", ip, procMap.size()));
+ break;
+ }
+ dumpProtoPreamble(protoStreams[shardIndex]);
+ }
+
final SparseArray uids = procMap.valueAt(ip);
for (int iu = 0; iu < uids.size(); iu++) {
final int uid = uids.keyAt(iu);
final ProcessState procState = uids.valueAt(iu);
- procState.dumpAggregatedProtoForStatsd(proto,
+ procState.dumpAggregatedProtoForStatsd(protoStreams[shardIndex],
ProcessStatsSectionProto.PROCESS_STATS,
- procName, uid, mTimePeriodEndRealtime, procToPkgMap);
+ procName, uid, mTimePeriodEndRealtime,
+ procToPkgMap, uidToPkgMap);
}
}
+
+ for (int i = 0; i <= shardIndex; i++) {
+ protoStreams[i].flush();
+ }
}
private void dumpProtoPreamble(ProtoOutputStream proto) {
@@ -2279,10 +2300,9 @@ public final class ProcessStats implements Parcelable {
/**
* Walk through the known processes and build up the process -> packages map if necessary.
*/
- public ProcessMap> collectProcessPackageMaps(
- String reqPackage, boolean activeOnly) {
- final ProcessMap> map = new ProcessMap<>();
-
+ private void collectProcessPackageMaps(String reqPackage, boolean activeOnly,
+ final ProcessMap> procToPkgMap,
+ final SparseArray> uidToPkgMap) {
final ArrayMap>> pkgMap =
mPackages.getMap();
for (int ip = pkgMap.size() - 1; ip >= 0; ip--) {
@@ -2304,17 +2324,22 @@ public final class ProcessStats implements Parcelable {
final String name = proc.getName();
final int uid = proc.getUid();
- ArraySet pkgStates = map.get(name, uid);
+ ArraySet pkgStates = procToPkgMap.get(name, uid);
if (pkgStates == null) {
pkgStates = new ArraySet<>();
- map.put(name, uid, pkgStates);
+ procToPkgMap.put(name, uid, pkgStates);
}
pkgStates.add(state);
+ ArraySet packages = uidToPkgMap.get(uid);
+ if (packages == null) {
+ packages = new ArraySet<>();
+ uidToPkgMap.put(uid, packages);
+ }
+ packages.add(state.mPackageName);
}
}
}
}
- return map;
}
/**
@@ -2329,10 +2354,12 @@ public final class ProcessStats implements Parcelable {
* @param now The timestamp when the dump was initiated.
* @param procState The target process where its association states should be dumped.
* @param proc2Pkg The map between process to packages running within it.
+ * @param uidToPkgMap The map between UID to packages with this UID
*/
public void dumpFilteredAssociationStatesProtoForProc(ProtoOutputStream proto,
long fieldId, long now, ProcessState procState,
- final ProcessMap> proc2Pkg) {
+ final ProcessMap> proc2Pkg,
+ final SparseArray> uidToPkgMap) {
if (procState.isMultiPackage() && procState.getCommonProcess() != procState) {
// It's a per-package process state, don't bother to write into statsd
return;
@@ -2395,8 +2422,12 @@ public final class ProcessStats implements Parcelable {
final SourceKey key = assocVals.keyAt(i);
final long[] vals = assocVals.valueAt(i);
final long token = proto.start(fieldId);
- proto.write(ProcessStatsAssociationProto.ASSOC_PROCESS_NAME, key.mProcess);
- proto.write(ProcessStatsAssociationProto.ASSOC_PACKAGE_NAME, key.mPackage);
+ final int idx = uidToPkgMap.indexOfKey(key.mUid);
+ ProcessState.writeCompressedProcessName(proto,
+ ProcessStatsAssociationProto.ASSOC_PROCESS_NAME,
+ key.mProcess, key.mPackage,
+ idx >= 0 && uidToPkgMap.valueAt(idx).size() > 1);
+ proto.write(ProcessStatsAssociationProto.ASSOC_UID, key.mUid);
proto.write(ProcessStatsAssociationProto.TOTAL_COUNT, (int) vals[1]);
proto.write(ProcessStatsAssociationProto.TOTAL_DURATION_SECS,
(int) (vals[0] / 1000));
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index e9d7d05fe68d0f08f7e4a70f95d1930d9509ba0b..167d128a76f80ec85a4c5e1d192f292645aa5814 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -709,7 +709,7 @@ public interface ServiceConnector {
if (DEBUG) {
return mDebugName;
}
- return mDelegate.toString() + " wrapped into " + super.toString();
+ return mDelegate + " wrapped into " + super.toString();
}
@Override
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index a660493f4613b81a64429546e0785ba53b385ce8..085cdfcf946233f75de4b97770c027416f145bf6 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -46,8 +46,10 @@ public final class InputMethodDebug {
return "UNSPECIFIED";
case StartInputReason.WINDOW_FOCUS_GAIN:
return "WINDOW_FOCUS_GAIN";
- case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY:
- return "WINDOW_FOCUS_GAIN_REPORT_ONLY";
+ case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR:
+ return "WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR";
+ case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR:
+ return "WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR";
case StartInputReason.APP_CALLED_RESTART_INPUT_API:
return "APP_CALLED_RESTART_INPUT_API";
case StartInputReason.CHECK_FOCUS:
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index 79397b81ace7da59c1b4278d587b2df9094570b1..4b968b45f1224a21e53f07543c9d849fbfad1332 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -46,7 +46,8 @@ import java.lang.annotation.Retention;
SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE,
SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME,
SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED,
- SoftInputShowHideReason.HIDE_RECENTS_ANIMATION})
+ SoftInputShowHideReason.HIDE_RECENTS_ANIMATION,
+ SoftInputShowHideReason.HIDE_BUBBLES})
public @interface SoftInputShowHideReason {
/** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
int SHOW_SOFT_INPUT = 0;
@@ -140,4 +141,10 @@ public @interface SoftInputShowHideReason {
* intercept touch from app window.
*/
int HIDE_RECENTS_ANIMATION = 18;
+
+ /**
+ * Hide soft input when {@link com.android.systemui.bubbles.BubbleController} is expanding,
+ * switching, or collapsing Bubbles.
+ */
+ int HIDE_BUBBLES = 19;
}
diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java
index a01c45919b8fc14643b02bc1fea854be08948e33..946ce858c12dff8f40349b03ac5e2fa84a8313b4 100644
--- a/core/java/com/android/internal/inputmethod/StartInputReason.java
+++ b/core/java/com/android/internal/inputmethod/StartInputReason.java
@@ -30,7 +30,8 @@ import java.lang.annotation.Retention;
@IntDef(value = {
StartInputReason.UNSPECIFIED,
StartInputReason.WINDOW_FOCUS_GAIN,
- StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY,
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR,
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR,
StartInputReason.APP_CALLED_RESTART_INPUT_API,
StartInputReason.CHECK_FOCUS,
StartInputReason.BOUND_TO_IMMS,
@@ -48,45 +49,51 @@ public @interface StartInputReason {
* to (re)start a new connection.
*/
int WINDOW_FOCUS_GAIN = 1;
+ /**
+ * {@link android.view.Window} gained focus and the focused view is same as current served
+ * view and its input connection remains. {@link android.view.inputmethod.InputMethodManager}
+ * just reports this window focus change event to sync IME input target for system.
+ */
+ int WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR = 2;
/**
* {@link android.view.Window} gained focus but there is no {@link android.view.View} that is
* eligible to have IME focus. {@link android.view.inputmethod.InputMethodManager} just reports
- * this window focus change event.
+ * this window focus change event for logging.
*/
- int WINDOW_FOCUS_GAIN_REPORT_ONLY = 2;
+ int WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR = 3;
/**
* {@link android.view.inputmethod.InputMethodManager#restartInput(android.view.View)} is
* either explicitly called by the application or indirectly called by some Framework class
* (e.g. {@link android.widget.EditText}).
*/
- int APP_CALLED_RESTART_INPUT_API = 3;
+ int APP_CALLED_RESTART_INPUT_API = 4;
/**
* {@link android.view.View} requested a new connection because of view focus change.
*/
- int CHECK_FOCUS = 4;
+ int CHECK_FOCUS = 5;
/**
* {@link android.view.inputmethod.InputMethodManager} is responding to
* {@link com.android.internal.view.IInputMethodClient#onBindMethod}.
*/
- int BOUND_TO_IMMS = 5;
+ int BOUND_TO_IMMS = 6;
/**
* {@link android.view.inputmethod.InputMethodManager} is responding to
* {@link com.android.internal.view.IInputMethodClient#onUnbindMethod}.
*/
- int UNBOUND_FROM_IMMS = 6;
+ int UNBOUND_FROM_IMMS = 7;
/**
* {@link android.view.inputmethod.InputMethodManager} is responding to
* {@link com.android.internal.view.IInputMethodClient#setActive}.
*/
- int ACTIVATED_BY_IMMS = 7;
+ int ACTIVATED_BY_IMMS = 8;
/**
* {@link android.view.inputmethod.InputMethodManager} is responding to
* {@link com.android.internal.view.IInputMethodClient#setActive}.
*/
- int DEACTIVATED_BY_IMMS = 8;
+ int DEACTIVATED_BY_IMMS = 9;
/**
* {@link com.android.server.inputmethod.InputMethodManagerService} is responding to
* {@link com.android.internal.view.IInputSessionCallback#sessionCreated}.
*/
- int SESSION_CREATED_BY_IME = 9;
+ int SESSION_CREATED_BY_IME = 10;
}
diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java
index 67ffd4d9340410b25ab0dc9df8d3751b5b8cf3df..5212265f6c8a3faf8846c71c332dfeaa8cf108b3 100644
--- a/core/java/com/android/internal/logging/UiEventLogger.java
+++ b/core/java/com/android/internal/logging/UiEventLogger.java
@@ -60,4 +60,28 @@ public interface UiEventLogger {
*/
void logWithInstanceId(@NonNull UiEventEnum event, int uid, @Nullable String packageName,
@Nullable InstanceId instance);
+
+ /**
+ * Log an event with ranked-choice information along with package.
+ * Does nothing if event.getId() <= 0.
+ * @param event an enum implementing UiEventEnum interface.
+ * @param uid the uid of the relevant app, if known (0 otherwise).
+ * @param packageName the package name of the relevant app, if known (null otherwise).
+ * @param position the position picked.
+ */
+ void logWithPosition(@NonNull UiEventEnum event, int uid, @Nullable String packageName,
+ int position);
+
+ /**
+ * Log an event with ranked-choice information along with package and instance ID.
+ * Does nothing if event.getId() <= 0.
+ * @param event an enum implementing UiEventEnum interface.
+ * @param uid the uid of the relevant app, if known (0 otherwise).
+ * @param packageName the package name of the relevant app, if known (null otherwise).
+ * @param instance An identifier obtained from an InstanceIdSequence. If null, reduces to
+ * logWithPosition().
+ * @param position the position picked.
+ */
+ void logWithInstanceIdAndPosition(@NonNull UiEventEnum event, int uid,
+ @Nullable String packageName, @Nullable InstanceId instance, int position);
}
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index 4d171ec8a3a82b229a2461ca095f4d8fb57c2fe7..c9156c13aae32ae60efbd6bd3f90042a195538ea 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -48,4 +48,31 @@ public class UiEventLoggerImpl implements UiEventLogger {
log(event, uid, packageName);
}
}
+
+ @Override
+ public void logWithPosition(UiEventEnum event, int uid, String packageName, int position) {
+ final int eventID = event.getId();
+ if (eventID > 0) {
+ FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED,
+ /* event_id = 1 */ eventID,
+ /* package_name = 2 */ packageName,
+ /* instance_id = 3 */ 0,
+ /* position_picked = 4 */ position);
+ }
+ }
+
+ @Override
+ public void logWithInstanceIdAndPosition(UiEventEnum event, int uid, String packageName,
+ InstanceId instance, int position) {
+ final int eventID = event.getId();
+ if ((eventID > 0) && (instance != null)) {
+ FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED,
+ /* event_id = 1 */ eventID,
+ /* package_name = 2 */ packageName,
+ /* instance_id = 3 */ instance.getId(),
+ /* position_picked = 4 */ position);
+ } else {
+ logWithPosition(event, uid, packageName, position);
+ }
+ }
}
diff --git a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
index 180ab0810f5b6f4e87e9d45de8ad79ea3e52bf07..2d09434807a6449db83cefabadef9ad083145cd2 100644
--- a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
+++ b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
@@ -35,13 +35,15 @@ public class UiEventLoggerFake implements UiEventLogger {
public final int eventId;
public final int uid;
public final String packageName;
- public final InstanceId instanceId; // Used only for WithInstanceId variant
+ public final InstanceId instanceId; // Used only for WithInstanceId variants
+ public final int position; // Used only for Position variants
FakeUiEvent(int eventId, int uid, String packageName) {
this.eventId = eventId;
this.uid = uid;
this.packageName = packageName;
this.instanceId = null;
+ this.position = 0;
}
FakeUiEvent(int eventId, int uid, String packageName, InstanceId instanceId) {
@@ -49,6 +51,15 @@ public class UiEventLoggerFake implements UiEventLogger {
this.uid = uid;
this.packageName = packageName;
this.instanceId = instanceId;
+ this.position = 0;
+ }
+
+ FakeUiEvent(int eventId, int uid, String packageName, InstanceId instanceId, int position) {
+ this.eventId = eventId;
+ this.uid = uid;
+ this.packageName = packageName;
+ this.instanceId = instanceId;
+ this.position = position;
}
}
@@ -92,4 +103,21 @@ public class UiEventLoggerFake implements UiEventLogger {
mLogs.add(new FakeUiEvent(eventId, uid, packageName, instance));
}
}
+
+ @Override
+ public void logWithPosition(UiEventEnum event, int uid, String packageName, int position) {
+ final int eventId = event.getId();
+ if (eventId > 0) {
+ mLogs.add(new FakeUiEvent(eventId, uid, packageName, null, position));
+ }
+ }
+
+ @Override
+ public void logWithInstanceIdAndPosition(UiEventEnum event, int uid, String packageName,
+ InstanceId instance, int position) {
+ final int eventId = event.getId();
+ if (eventId > 0) {
+ mLogs.add(new FakeUiEvent(eventId, uid, packageName, instance, position));
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index ab0cc3093b6dc4857331d8a75d5a6148b203ec6c..2393036b5a3898dc6999db53ced9ad7b76f2738c 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -210,7 +210,7 @@ public class FuseAppLoop implements Handler.Callback {
if (mInstance != 0) {
native_replySimple(mInstance, unique, FUSE_OK);
}
- mBytesMap.stopUsing(entry.getThreadId());
+ mBytesMap.stopUsing(inode);
recycleLocked(args);
}
break;
@@ -270,7 +270,7 @@ public class FuseAppLoop implements Handler.Callback {
if (mInstance != 0) {
native_replyOpen(mInstance, unique, /* fh */ inode);
entry.opened = true;
- return mBytesMap.startUsing(entry.getThreadId());
+ return mBytesMap.startUsing(inode);
}
} catch (ErrnoException error) {
replySimpleLocked(unique, getError(error));
@@ -354,27 +354,27 @@ public class FuseAppLoop implements Handler.Callback {
}
/**
- * Map between Thread ID and byte buffer.
+ * Map between inode and byte buffer.
*/
private static class BytesMap {
final Map mEntries = new HashMap<>();
- byte[] startUsing(long threadId) {
- BytesMapEntry entry = mEntries.get(threadId);
+ byte[] startUsing(long inode) {
+ BytesMapEntry entry = mEntries.get(inode);
if (entry == null) {
entry = new BytesMapEntry();
- mEntries.put(threadId, entry);
+ mEntries.put(inode, entry);
}
entry.counter++;
return entry.bytes;
}
- void stopUsing(long threadId) {
- final BytesMapEntry entry = mEntries.get(threadId);
+ void stopUsing(long inode) {
+ final BytesMapEntry entry = mEntries.get(inode);
Objects.requireNonNull(entry);
entry.counter--;
if (entry.counter <= 0) {
- mEntries.remove(threadId);
+ mEntries.remove(inode);
}
}
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java b/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java
index c11b939098c4aa19680d4bd2290ad5b16e5f3e07..69ca9922e81f6f36c7498864512433cce55b55fc 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java
@@ -59,7 +59,7 @@ import java.util.Objects;
* #getProcessCpuUsageDiffed()} result.
*
*
Thresholding is done in this class, instead of {@link KernelCpuThreadReader}, and instead of
- * WestWorld, because the thresholding should be done after diffing, not before. This is because of
+ * statsd, because the thresholding should be done after diffing, not before. This is because of
* two issues with thresholding before diffing:
*
*
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 505a05eb9c234c450fb20450406430fe574a9c51..a7d9827855a2108ebdc8ebc80f9927a4d5d5df7e 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -24,6 +24,7 @@ import android.content.pm.ApplicationInfo;
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
+import android.net.NetworkUtils;
import android.os.FactoryTest;
import android.os.IVold;
import android.os.Process;
@@ -286,6 +287,13 @@ public final class Zygote {
private Zygote() {}
+ private static boolean containsInetGid(int[] gids) {
+ for (int i = 0; i < gids.length; i++) {
+ if (gids[i] == android.os.Process.INET_GID) return true;
+ }
+ return false;
+ }
+
/**
* Forks a new VM instance. The current VM must have been started
* with the -Xzygote flag. NOTE: new instance keeps all
@@ -341,6 +349,11 @@ public final class Zygote {
if (pid == 0) {
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
+
+ // If no GIDs were specified, don't make any permissions changes based on groups.
+ if (gids != null && gids.length > 0) {
+ NetworkUtils.setAllowNetworkingForProcess(containsInetGid(gids));
+ }
}
// Set the Java Language thread priority to the default value for new apps.
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 7bfed91c42b921513c1f9c01983b286fce8e1510..6fe1d8140371ee2c406d8d4c1884c5e285118d19 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -16,6 +16,7 @@
package com.android.internal.policy;
+import android.graphics.Insets;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
@@ -69,16 +70,14 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
private ColorDrawable mNavigationBarColor;
private boolean mOldFullscreen;
private boolean mFullscreen;
- private final Rect mOldSystemInsets = new Rect();
- private final Rect mOldStableInsets = new Rect();
- private final Rect mSystemInsets = new Rect();
- private final Rect mStableInsets = new Rect();
+ private final Rect mOldSystemBarInsets = new Rect();
+ private final Rect mSystemBarInsets = new Rect();
private final Rect mTmpRect = new Rect();
public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor,
- boolean fullscreen, Rect systemInsets, Rect stableInsets) {
+ boolean fullscreen, Insets systemBarInsets) {
setName("ResizeFrame");
mRenderer = renderer;
@@ -95,10 +94,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
mTargetRect.set(initialBounds);
mFullscreen = fullscreen;
mOldFullscreen = fullscreen;
- mSystemInsets.set(systemInsets);
- mStableInsets.set(stableInsets);
- mOldSystemInsets.set(systemInsets);
- mOldStableInsets.set(stableInsets);
+ mSystemBarInsets.set(systemBarInsets.toRect());
+ mOldSystemBarInsets.set(systemBarInsets.toRect());
// Kick off our draw thread.
start();
@@ -154,16 +151,13 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
*
* @param newTargetBounds The new target bounds.
* @param fullscreen Whether the window is currently drawing in fullscreen.
- * @param systemInsets The current visible system insets for the window.
- * @param stableInsets The stable insets for the window.
+ * @param systemBarInsets The current visible system insets for the window.
*/
- public void setTargetRect(Rect newTargetBounds, boolean fullscreen, Rect systemInsets,
- Rect stableInsets) {
+ public void setTargetRect(Rect newTargetBounds, boolean fullscreen, Rect systemBarInsets) {
synchronized (this) {
mFullscreen = fullscreen;
mTargetRect.set(newTargetBounds);
- mSystemInsets.set(systemInsets);
- mStableInsets.set(stableInsets);
+ mSystemBarInsets.set(systemBarInsets);
// Notify of a bounds change.
pingRenderLocked(false /* drawImmediate */);
}
@@ -247,14 +241,12 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
mNewTargetRect.set(mTargetRect);
if (!mNewTargetRect.equals(mOldTargetRect)
|| mOldFullscreen != mFullscreen
- || !mStableInsets.equals(mOldStableInsets)
- || !mSystemInsets.equals(mOldSystemInsets)
+ || !mSystemBarInsets.equals(mOldSystemBarInsets)
|| mReportNextDraw) {
mOldFullscreen = mFullscreen;
mOldTargetRect.set(mNewTargetRect);
- mOldSystemInsets.set(mSystemInsets);
- mOldStableInsets.set(mStableInsets);
- redrawLocked(mNewTargetRect, mFullscreen, mSystemInsets, mStableInsets);
+ mOldSystemBarInsets.set(mSystemBarInsets);
+ redrawLocked(mNewTargetRect, mFullscreen);
}
}
@@ -304,11 +296,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
*
* @param newBounds The window bounds which needs to be drawn.
* @param fullscreen Whether the window is currently drawing in fullscreen.
- * @param systemInsets The current visible system insets for the window.
- * @param stableInsets The stable insets for the window.
*/
- private void redrawLocked(Rect newBounds, boolean fullscreen, Rect systemInsets,
- Rect stableInsets) {
+ private void redrawLocked(Rect newBounds, boolean fullscreen) {
// While a configuration change is taking place the view hierarchy might become
// inaccessible. For that case we remember the previous metrics to avoid flashes.
@@ -355,7 +344,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
}
mFrameAndBackdropNode.endRecording();
- drawColorViews(left, top, width, height, fullscreen, systemInsets, stableInsets);
+ drawColorViews(left, top, width, height, fullscreen);
// We need to render the node explicitly
mRenderer.drawRenderNode(mFrameAndBackdropNode);
@@ -363,14 +352,13 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
reportDrawIfNeeded();
}
- private void drawColorViews(int left, int top, int width, int height,
- boolean fullscreen, Rect systemInsets, Rect stableInsets) {
+ private void drawColorViews(int left, int top, int width, int height, boolean fullscreen) {
if (mSystemBarBackgroundNode == null) {
return;
}
RecordingCanvas canvas = mSystemBarBackgroundNode.beginRecording(width, height);
mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
- final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top);
+ final int topInset = mSystemBarInsets.top;
if (mStatusBarColor != null) {
mStatusBarColor.setBounds(0, 0, left + width, topInset);
mStatusBarColor.draw(canvas);
@@ -380,7 +368,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
// don't want the navigation bar background be moving around when resizing in docked mode.
// However, we need it for the transitions into/out of docked mode.
if (mNavigationBarColor != null && fullscreen) {
- DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect, 1f);
+ DecorView.getNavigationBarRect(width, height, mSystemBarInsets, mTmpRect, 1f);
mNavigationBarColor.setBounds(mTmpRect);
mNavigationBarColor.draw(canvas);
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index c6135f2c81d3f57861f5a1b454f6fc8601bbf3d4..b12c5e9ba5b056fb42034301c6f5861e44dfbb08 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1050,22 +1050,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
return false;
}
- public static int getColorViewTopInset(int stableTop, int systemTop) {
- return Math.min(stableTop, systemTop);
- }
-
- public static int getColorViewBottomInset(int stableBottom, int systemBottom) {
- return Math.min(stableBottom, systemBottom);
- }
-
- public static int getColorViewRightInset(int stableRight, int systemRight) {
- return Math.min(stableRight, systemRight);
- }
-
- public static int getColorViewLeftInset(int stableLeft, int systemLeft) {
- return Math.min(stableLeft, systemLeft);
- }
-
public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
return bottomInset == 0 && rightInset > 0;
}
@@ -1079,14 +1063,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
: isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
}
- public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
- Rect contentInsets, Rect outRect, float scale) {
- final int bottomInset =
- (int) (getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom) * scale);
- final int leftInset =
- (int) (getColorViewLeftInset(stableInsets.left, contentInsets.left) * scale);
- final int rightInset =
- (int) (getColorViewLeftInset(stableInsets.right, contentInsets.right) * scale);
+ public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect systemBarInsets,
+ Rect outRect, float scale) {
+ final int bottomInset = (int) (systemBarInsets.bottom * scale);
+ final int leftInset = (int) (systemBarInsets.left * scale);
+ final int rightInset = (int) (systemBarInsets.right * scale);
final int size = getNavBarSize(bottomInset, rightInset, leftInset);
if (isNavBarToRightEdge(bottomInset, rightInset)) {
outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
@@ -1113,31 +1094,30 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
mLastWindowFlags = attrs.flags;
if (insets != null) {
- mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
- insets.getSystemWindowInsetTop());
- mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
- insets.getSystemWindowInsetBottom());
- mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
- insets.getSystemWindowInsetRight());
- mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
- insets.getSystemWindowInsetLeft());
+ final Insets systemBarInsets = insets.getInsets(WindowInsets.Type.systemBars());
+ final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.systemBars());
+ mLastTopInset = systemBarInsets.top;
+ mLastBottomInset = systemBarInsets.bottom;
+ mLastRightInset = systemBarInsets.right;
+ mLastLeftInset = systemBarInsets.left;
// Don't animate if the presence of stable insets has changed, because that
// indicates that the window was either just added and received them for the
// first time, or the window size or position has changed.
- boolean hasTopStableInset = insets.getStableInsetTop() != 0;
+ boolean hasTopStableInset = stableBarInsets.top != 0;
disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
mLastHasTopStableInset = hasTopStableInset;
- boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
+ boolean hasBottomStableInset = stableBarInsets.bottom != 0;
disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
mLastHasBottomStableInset = hasBottomStableInset;
- boolean hasRightStableInset = insets.getStableInsetRight() != 0;
+ boolean hasRightStableInset = stableBarInsets.right != 0;
disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
mLastHasRightStableInset = hasRightStableInset;
- boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
+ boolean hasLeftStableInset = stableBarInsets.left != 0;
disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
mLastHasLeftStableInset = hasLeftStableInset;
@@ -2296,7 +2276,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
Rect stableInsets) {
if (mBackdropFrameRenderer != null) {
- mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
+ mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets);
}
}
@@ -2314,11 +2294,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
final ThreadedRenderer renderer = getThreadedRenderer();
if (renderer != null) {
loadBackgroundDrawablesIfNeeded();
+ WindowInsets rootInsets = getRootWindowInsets();
mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
- getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
- stableInsets);
+ getCurrentColor(mNavigationColorViewState), fullscreen,
+ rootInsets.getInsets(WindowInsets.Type.systemBars()));
// Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
// If we want to get the shadow shown while resizing, we would need to elevate a new
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 575a5320bbd3ce4d43fe25c8615ffff24bb4d442..527286cf000ef8f0958f42bd761e2a2ed29c8816 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -104,17 +104,18 @@ public class DividerSnapAlgorithm {
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
boolean isHorizontalDivision, Rect insets) {
this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets,
- DOCKED_INVALID, false);
+ DOCKED_INVALID, false /* minimized */, true /* resizable */);
}
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
boolean isHorizontalDivision, Rect insets, int dockSide) {
this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets,
- dockSide, false);
+ dockSide, false /* minimized */, true /* resizable */);
}
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
- boolean isHorizontalDivision, Rect insets, int dockSide, boolean isMinimizedMode) {
+ boolean isHorizontalDivision, Rect insets, int dockSide, boolean isMinimizedMode,
+ boolean isHomeResizable) {
mMinFlingVelocityPxPerSecond =
MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
mMinDismissVelocityPxPerSecond =
@@ -132,8 +133,8 @@ public class DividerSnapAlgorithm {
com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
mMinimalSizeResizableTask = res.getDimensionPixelSize(
com.android.internal.R.dimen.default_minimal_size_resizable_task);
- mTaskHeightInMinimizedMode = res.getDimensionPixelSize(
- com.android.internal.R.dimen.task_height_of_minimized_mode);
+ mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
+ com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
calculateTargets(isHorizontalDivision, dockSide);
mFirstSplitTarget = mTargets.get(1);
mLastSplitTarget = mTargets.get(mTargets.size() - 2);
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index c5729b05c5878fc10aa755831708c1ac32cb26ae..046981cf2e8f92ee171dd8eda07f6a492f488ad7 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -21,8 +21,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COM
import static android.view.View.SYSTEM_UI_LAYOUT_FLAGS;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowInsets.Type.ime;
-import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
@@ -34,8 +32,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
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.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -146,17 +142,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if ((view.getWindowSystemUiVisibility() & SYSTEM_UI_LAYOUT_FLAGS) != 0) {
return new Pair<>(Insets.NONE, insets);
}
-
- boolean includeIme = (view.getViewRootImpl().mWindowAttributes.softInputMode
- & SOFT_INPUT_MASK_ADJUST)
- == SOFT_INPUT_ADJUST_RESIZE;
- Insets insetsToApply;
- if (ViewRootImpl.sNewInsetsMode == 0) {
- insetsToApply = insets.getSystemWindowInsets();
- } else {
- insetsToApply = insets.getInsets(systemBars() | (includeIme ? ime() : 0));
- }
- insets = insets.inset(insetsToApply);
+ Insets insetsToApply = insets.getSystemWindowInsets();
return new Pair<>(insetsToApply,
insets.inset(insetsToApply).consumeSystemWindowInsets());
};
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index c32082418bc59829e949cc8252f0d5d646ac88d8..4999ec0556087e41bad0a8c8a98949c5358767f6 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -79,6 +79,7 @@ interface IStatusBarService
void onNotificationSettingsViewed(String key);
void onNotificationBubbleChanged(String key, boolean isBubble, int flags);
void onBubbleNotificationSuppressionChanged(String key, boolean isSuppressed);
+ void hideCurrentInputMethodForBubbles();
void grantInlineReplyUriPermission(String key, in Uri uri, in UserHandle user, String packageName);
void clearInlineReplyUriPermissions(String key);
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index ad6c7e8f7f60d6f933ad22c81a6223e03cd39911..9bf05135c4c5ccb7e859b5416402cc4855310742 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -8,10 +8,10 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -37,10 +37,12 @@ public class ScreenshotHelper {
private int mSource;
private boolean mHasStatusBar;
private boolean mHasNavBar;
- private Bitmap mBitmap;
+ private Bundle mBitmapBundle;
private Rect mBoundsInScreen;
private Insets mInsets;
private int mTaskId;
+ private int mUserId;
+ private ComponentName mTopComponent;
ScreenshotRequest(int source, boolean hasStatus, boolean hasNav) {
mSource = source;
@@ -48,24 +50,29 @@ public class ScreenshotHelper {
mHasNavBar = hasNav;
}
- ScreenshotRequest(
- int source, Bitmap bitmap, Rect boundsInScreen, Insets insets, int taskId) {
+ ScreenshotRequest(int source, Bundle bitmapBundle, Rect boundsInScreen, Insets insets,
+ int taskId, int userId, ComponentName topComponent) {
mSource = source;
- mBitmap = bitmap;
+ mBitmapBundle = bitmapBundle;
mBoundsInScreen = boundsInScreen;
mInsets = insets;
mTaskId = taskId;
+ mUserId = userId;
+ mTopComponent = topComponent;
}
ScreenshotRequest(Parcel in) {
mSource = in.readInt();
mHasStatusBar = in.readBoolean();
mHasNavBar = in.readBoolean();
+
if (in.readInt() == 1) {
- mBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ mBitmapBundle = in.readBundle(getClass().getClassLoader());
mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader());
mInsets = in.readParcelable(Insets.class.getClassLoader());
mTaskId = in.readInt();
+ mUserId = in.readInt();
+ mTopComponent = in.readParcelable(ComponentName.class.getClassLoader());
}
}
@@ -81,8 +88,8 @@ public class ScreenshotHelper {
return mHasNavBar;
}
- public Bitmap getBitmap() {
- return mBitmap;
+ public Bundle getBitmapBundle() {
+ return mBitmapBundle;
}
public Rect getBoundsInScreen() {
@@ -97,6 +104,15 @@ public class ScreenshotHelper {
return mTaskId;
}
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public ComponentName getTopComponent() {
+ return mTopComponent;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -107,14 +123,16 @@ public class ScreenshotHelper {
dest.writeInt(mSource);
dest.writeBoolean(mHasStatusBar);
dest.writeBoolean(mHasNavBar);
- if (mBitmap == null) {
+ if (mBitmapBundle == null) {
dest.writeInt(0);
} else {
dest.writeInt(1);
- dest.writeParcelable(mBitmap, 0);
+ dest.writeBundle(mBitmapBundle);
dest.writeParcelable(mBoundsInScreen, 0);
dest.writeParcelable(mInsets, 0);
dest.writeInt(mTaskId);
+ dest.writeInt(mUserId);
+ dest.writeParcelable(mTopComponent, 0);
}
}
@@ -234,19 +252,22 @@ public class ScreenshotHelper {
/**
* Request that provided image be handled as if it was a screenshot.
*
- * @param screenshot The bitmap to treat as the screen shot.
+ * @param screenshotBundle Bundle containing the buffer and color space of the screenshot.
* @param boundsInScreen The bounds in screen coordinates that the bitmap orginated from.
* @param insets The insets that the image was shown with, inside the screenbounds.
* @param taskId The taskId of the task that the screen shot was taken of.
+ * @param userId The userId of user running the task provided in taskId.
+ * @param topComponent The component name of the top component running in the task.
* @param handler A handler used in case the screenshot times out
* @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the
* screenshot was taken.
*/
- public void provideScreenshot(@NonNull Bitmap screenshot, @NonNull Rect boundsInScreen,
- @NonNull Insets insets, int taskId, int source,
+ public void provideScreenshot(@NonNull Bundle screenshotBundle, @NonNull Rect boundsInScreen,
+ @NonNull Insets insets, int taskId, int userId, ComponentName topComponent, int source,
@NonNull Handler handler, @Nullable Consumer completionConsumer) {
ScreenshotRequest screenshotRequest =
- new ScreenshotRequest(source, screenshot, boundsInScreen, insets, taskId);
+ new ScreenshotRequest(source, screenshotBundle, boundsInScreen, insets, taskId,
+ userId, topComponent);
takeScreenshot(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_TIMEOUT_MS,
handler, screenshotRequest, completionConsumer);
}
@@ -295,7 +316,7 @@ public class ScreenshotHelper {
};
msg.replyTo = new Messenger(h);
- if (mScreenshotConnection == null) {
+ if (mScreenshotConnection == null || mScreenshotService == null) {
final ComponentName serviceComponent = ComponentName.unflattenFromString(
mContext.getResources().getString(
com.android.internal.R.string.config_screenshotServiceComponent));
@@ -330,8 +351,11 @@ public class ScreenshotHelper {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
mScreenshotService = null;
- handler.removeCallbacks(mScreenshotTimeout);
- notifyScreenshotError();
+ // only log an error if we're still within the timeout period
+ if (handler.hasCallbacks(mScreenshotTimeout)) {
+ handler.removeCallbacks(mScreenshotTimeout);
+ notifyScreenshotError();
+ }
}
}
}
diff --git a/core/java/com/android/internal/util/SyncResultReceiver.java b/core/java/com/android/internal/util/SyncResultReceiver.java
index 00e91017a9ebc7ce6cd104042989bac4c6ed76cf..6b1358293d02e34fcbbd090e027d7a88dc735186 100644
--- a/core/java/com/android/internal/util/SyncResultReceiver.java
+++ b/core/java/com/android/internal/util/SyncResultReceiver.java
@@ -182,7 +182,7 @@ public final class SyncResultReceiver extends IResultReceiver.Stub {
}
/** @hide */
- public static final class TimeoutException extends RuntimeException {
+ public static final class TimeoutException extends Exception {
private TimeoutException(String msg) {
super(msg);
}
diff --git a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
index cf1220c08467c91384d73da93facfa30ab020eeb..6c97962ac057205b4674057dfc0151f622592bf4 100644
--- a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
+++ b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
@@ -22,40 +22,56 @@ import android.view.inputmethod.InlineSuggestionsRequest;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
/**
- * Binder interface for the IME service to send an inline suggestion request to the system.
+ * Binder interface for the IME service to send {@link InlineSuggestionsRequest} or notify other IME
+ * service events to the system.
* {@hide}
*/
oneway interface IInlineSuggestionsRequestCallback {
- // Indicates that the current IME does not support inline suggestion.
+ /** Indicates that the current IME does not support inline suggestion. */
void onInlineSuggestionsUnsupported();
- // Sends the inline suggestions request from IME to Autofill. Calling this method indicates
- // that the IME input is started on the view corresponding to the request.
+ /**
+ * Sends the inline suggestions request from IME to Autofill. Calling this method indicates
+ * that the IME input is started on the view corresponding to the request.
+ */
void onInlineSuggestionsRequest(in InlineSuggestionsRequest request,
in IInlineSuggestionsResponseCallback callback);
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onStartInput(EditorInfo, boolean)} is called on the given focused field.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onStartInput(EditorInfo, boolean)} is called on the given focused field.
+ */
void onInputMethodStartInput(in AutofillId imeFieldId);
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #dispatchOnShowInputRequested(int, boolean)} is called and shares the call result.
- // The true value of {@code requestResult} means the IME is about to be shown, while
- // false value means the IME will not be shown.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #dispatchOnShowInputRequested(int, boolean)} is called and shares the call result.
+ * The true value of {@code requestResult} means the IME is about to be shown, while
+ * false value means the IME will not be shown.
+ */
void onInputMethodShowInputRequested(boolean requestResult);
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onStartInputView(EditorInfo, boolean)} is called on the field specified by the earlier
- // {@link #onInputMethodStartInput(AutofillId)}.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onStartInputView(EditorInfo, boolean)} is called on the field specified by the earlier
+ * {@link #onInputMethodStartInput(AutofillId)}.
+ */
void onInputMethodStartInputView();
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onFinishInputView(boolean)} is called on the field specified by the earlier
- // {@link #onInputMethodStartInput(AutofillId)}.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onFinishInputView(boolean)} is called on the field specified by the earlier
+ * {@link #onInputMethodStartInput(AutofillId)}.
+ */
void onInputMethodFinishInputView();
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onFinishInput()} is called on the field specified by the earlier
- // {@link #onInputMethodStartInput(AutofillId)}.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onFinishInput()} is called on the field specified by the earlier
+ * {@link #onInputMethodStartInput(AutofillId)}.
+ */
void onInputMethodFinishInput();
+
+ // Indicates that the current IME changes inline suggestion session.
+ void onInlineSuggestionsSessionInvalidated();
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 3f03f2a3e75450ca0aedd578529f12c5df1af9ab..a1cbd3fcae79ae3c7201570216c5b96bbbb18305 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -71,4 +71,10 @@ interface IInputMethodManager {
void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
in float[] matrixValues);
+
+ oneway void reportPerceptible(in IBinder windowToken, boolean perceptible);
+ /** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */
+ void removeImeSurface();
+ /** Remove the IME surface. Requires passing the currently focused window. */
+ void removeImeSurfaceFromWindow(in IBinder windowToken);
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 688e00bc5a2993537241c5d419adc25fa43e394a..0791ed3c42eca5395e9dedd882011c40987f4a8c 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -21,6 +21,10 @@ import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_
import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN;
import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
import android.annotation.AttrRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,6 +40,7 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
+import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcelable;
@@ -67,6 +72,7 @@ import com.android.internal.util.ContrastColorUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Pattern;
@@ -80,7 +86,7 @@ public class ConversationLayout extends FrameLayout
private static final float COLOR_SHIFT_AMOUNT = 60;
/**
- * Pattren for filter some ingonable characters.
+ * Pattern for filter some ignorable characters.
* p{Z} for any kind of whitespace or invisible separator.
* p{C} for any kind of punctuation character.
*/
@@ -93,8 +99,12 @@ public class ConversationLayout extends FrameLayout
public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+ public static final Interpolator OVERSHOOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
public static final OnLayoutChangeListener MESSAGING_PROPERTY_ANIMATOR
= new MessagingPropertyAnimator();
+ public static final int IMPORTANCE_ANIM_GROW_DURATION = 250;
+ public static final int IMPORTANCE_ANIM_SHRINK_DURATION = 200;
+ public static final int IMPORTANCE_ANIM_SHRINK_DELAY = 25;
private List mMessages = new ArrayList<>();
private List mHistoricMessages = new ArrayList<>();
private MessagingLinearLayout mMessagingLinearLayout;
@@ -161,6 +171,7 @@ public class ConversationLayout extends FrameLayout
private Rect mAppOpsTouchRect = new Rect();
private float mMinTouchSize;
private Icon mConversationIcon;
+ private Icon mShortcutIcon;
private View mAppNameDivider;
public ConversationLayout(@NonNull Context context) {
@@ -186,7 +197,6 @@ public class ConversationLayout extends FrameLayout
super.onFinishInflate();
mMessagingLinearLayout = findViewById(R.id.notification_messaging);
mActions = findViewById(R.id.actions);
- mMessagingLinearLayout.setMessagingLayout(this);
mImageMessageContainer = findViewById(R.id.conversation_image_message_container);
// We still want to clip, but only on the top, since views can temporarily out of bounds
// during transitions.
@@ -222,13 +232,20 @@ public class ConversationLayout extends FrameLayout
oldVisibility = mImportanceRingView.getVisibility();
wasGone = oldVisibility == GONE;
visibility = !mImportantConversation ? GONE : visibility;
- isGone = visibility == GONE;
- if (wasGone != isGone) {
+ boolean isRingGone = visibility == GONE;
+ if (wasGone != isRingGone) {
// Keep the badge visibility in sync with the icon. This is necessary in cases
// Where the icon is being hidden externally like in group children.
mImportanceRingView.animate().cancel();
mImportanceRingView.setVisibility(visibility);
}
+
+ oldVisibility = mConversationIconBadge.getVisibility();
+ wasGone = oldVisibility == GONE;
+ if (wasGone != isGone) {
+ mConversationIconBadge.animate().cancel();
+ mConversationIconBadge.setVisibility(visibility);
+ }
});
// When the small icon is gone, hide the rest of the badge
mIcon.setOnForceHiddenChangedListener((forceHidden) -> {
@@ -330,14 +347,74 @@ public class ConversationLayout extends FrameLayout
mNameReplacement = nameReplacement;
}
- /**
- * Sets this conversation as "important", adding some additional UI treatment.
- */
+ /** Sets this conversation as "important", adding some additional UI treatment. */
@RemotableViewMethod
public void setIsImportantConversation(boolean isImportantConversation) {
+ setIsImportantConversation(isImportantConversation, false);
+ }
+
+ /** @hide **/
+ public void setIsImportantConversation(boolean isImportantConversation, boolean animate) {
mImportantConversation = isImportantConversation;
- mImportanceRingView.setVisibility(isImportantConversation
- && mIcon.getVisibility() != GONE ? VISIBLE : GONE);
+ mImportanceRingView.setVisibility(isImportantConversation && mIcon.getVisibility() != GONE
+ ? VISIBLE : GONE);
+
+ if (animate && isImportantConversation) {
+ GradientDrawable ring = (GradientDrawable) mImportanceRingView.getDrawable();
+ ring.mutate();
+ GradientDrawable bg = (GradientDrawable) mConversationIconBadgeBg.getDrawable();
+ bg.mutate();
+ int ringColor = getResources()
+ .getColor(R.color.conversation_important_highlight);
+ int standardThickness = getResources()
+ .getDimensionPixelSize(R.dimen.importance_ring_stroke_width);
+ int largeThickness = getResources()
+ .getDimensionPixelSize(R.dimen.importance_ring_anim_max_stroke_width);
+ int standardSize = getResources().getDimensionPixelSize(
+ R.dimen.importance_ring_size);
+ int baseSize = standardSize - standardThickness * 2;
+ int bgSize = getResources()
+ .getDimensionPixelSize(R.dimen.conversation_icon_size_badged);
+
+ ValueAnimator.AnimatorUpdateListener animatorUpdateListener = animation -> {
+ int strokeWidth = Math.round((float) animation.getAnimatedValue());
+ ring.setStroke(strokeWidth, ringColor);
+ int newSize = baseSize + strokeWidth * 2;
+ ring.setSize(newSize, newSize);
+ mImportanceRingView.invalidate();
+ };
+
+ ValueAnimator growAnimation = ValueAnimator.ofFloat(0, largeThickness);
+ growAnimation.setInterpolator(LINEAR_OUT_SLOW_IN);
+ growAnimation.setDuration(IMPORTANCE_ANIM_GROW_DURATION);
+ growAnimation.addUpdateListener(animatorUpdateListener);
+
+ ValueAnimator shrinkAnimation =
+ ValueAnimator.ofFloat(largeThickness, standardThickness);
+ shrinkAnimation.setDuration(IMPORTANCE_ANIM_SHRINK_DURATION);
+ shrinkAnimation.setStartDelay(IMPORTANCE_ANIM_SHRINK_DELAY);
+ shrinkAnimation.setInterpolator(OVERSHOOT);
+ shrinkAnimation.addUpdateListener(animatorUpdateListener);
+ shrinkAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Shrink the badge bg so that it doesn't peek behind the animation
+ bg.setSize(baseSize, baseSize);
+ mConversationIconBadgeBg.invalidate();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Reset bg back to normal size
+ bg.setSize(bgSize, bgSize);
+ mConversationIconBadgeBg.invalidate();
+ }
+ });
+
+ AnimatorSet anims = new AnimatorSet();
+ anims.playSequentially(growAnimation, shrinkAnimation);
+ anims.start();
+ }
}
public boolean isImportantConversation() {
@@ -465,10 +542,9 @@ public class ConversationLayout extends FrameLayout
private void updateConversationLayout() {
// Set avatar and name
CharSequence conversationText = mConversationTitle;
+ mConversationIcon = mShortcutIcon;
if (mIsOneToOne) {
// Let's resolve the icon / text from the last sender
- mConversationIconView.setVisibility(VISIBLE);
- mConversationFacePile.setVisibility(GONE);
CharSequence userKey = getKey(mUser);
for (int i = mGroups.size() - 1; i >= 0; i--) {
MessagingGroup messagingGroup = mGroups.get(i);
@@ -480,30 +556,31 @@ public class ConversationLayout extends FrameLayout
// (This usually happens for most 1:1 conversations)
conversationText = messagingGroup.getSenderName();
}
- Icon avatarIcon = messagingGroup.getAvatarIcon();
- if (avatarIcon == null) {
- avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor);
+ if (mConversationIcon == null) {
+ Icon avatarIcon = messagingGroup.getAvatarIcon();
+ if (avatarIcon == null) {
+ avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor);
+ }
+ mConversationIcon = avatarIcon;
}
- mConversationIcon = avatarIcon;
- mConversationIconView.setImageIcon(mConversationIcon);
break;
}
}
+ }
+ if (mConversationIcon == null) {
+ mConversationIcon = mLargeIcon;
+ }
+ if (mIsOneToOne || mConversationIcon != null) {
+ mConversationIconView.setVisibility(VISIBLE);
+ mConversationFacePile.setVisibility(GONE);
+ mConversationIconView.setImageIcon(mConversationIcon);
} else {
- if (mLargeIcon != null) {
- mConversationIcon = mLargeIcon;
- mConversationIconView.setVisibility(VISIBLE);
- mConversationFacePile.setVisibility(GONE);
- mConversationIconView.setImageIcon(mLargeIcon);
- } else {
- mConversationIcon = null;
- mConversationIconView.setVisibility(GONE);
- // This will also inflate it!
- mConversationFacePile.setVisibility(VISIBLE);
- // rebind the value to the inflated view instead of the stub
- mConversationFacePile = findViewById(R.id.conversation_face_pile);
- bindFacePile();
- }
+ mConversationIconView.setVisibility(GONE);
+ // This will also inflate it!
+ mConversationFacePile.setVisibility(VISIBLE);
+ // rebind the value to the inflated view instead of the stub
+ mConversationFacePile = findViewById(R.id.conversation_face_pile);
+ bindFacePile();
}
if (TextUtils.isEmpty(conversationText)) {
conversationText = mIsOneToOne ? mFallbackChatName : mFallbackGroupChatName;
@@ -691,9 +768,13 @@ public class ConversationLayout extends FrameLayout
// group
: mExpandedGroupMessagePadding;
+ int iconPadding = mIsOneToOne || mIsCollapsed
+ ? mConversationIconTopPadding
+ : mConversationIconTopPaddingExpandedGroup;
+
mConversationIconContainer.setPaddingRelative(
mConversationIconContainer.getPaddingStart(),
- mConversationIconTopPadding,
+ iconPadding,
mConversationIconContainer.getPaddingEnd(),
mConversationIconContainer.getPaddingBottom());
@@ -709,6 +790,11 @@ public class ConversationLayout extends FrameLayout
mLargeIcon = largeIcon;
}
+ @RemotableViewMethod
+ public void setShortcutIcon(Icon shortcutIcon) {
+ mShortcutIcon = shortcutIcon;
+ }
+
/**
* Sets the conversation title of this conversation.
*
@@ -716,7 +802,8 @@ public class ConversationLayout extends FrameLayout
*/
@RemotableViewMethod
public void setConversationTitle(CharSequence conversationTitle) {
- mConversationTitle = conversationTitle;
+ // Remove formatting from the title.
+ mConversationTitle = conversationTitle != null ? conversationTitle.toString() : null;
}
public CharSequence getConversationTitle() {
@@ -973,6 +1060,9 @@ public class ConversationLayout extends FrameLayout
groups.add(currentGroup);
if (sender == null) {
sender = mUser;
+ } else {
+ // Remove all formatting from the sender name
+ sender = sender.toBuilder().setName(Objects.toString(sender.getName())).build();
}
senders.add(sender);
currentSenderKey = key;
@@ -1170,7 +1260,6 @@ public class ConversationLayout extends FrameLayout
}
private void updateContentEndPaddings() {
-
// Let's make sure the conversation header can't run into the expand button when we're
// collapsed and update the paddings of the content
int headerPaddingEnd;
@@ -1215,9 +1304,10 @@ public class ConversationLayout extends FrameLayout
if (expandable) {
mExpandButtonContainer.setVisibility(VISIBLE);
mExpandButtonInnerContainer.setOnClickListener(onClickListener);
+ mConversationIconContainer.setOnClickListener(onClickListener);
} else {
- // TODO: handle content paddings to end of layout
mExpandButtonContainer.setVisibility(GONE);
+ mConversationIconContainer.setOnClickListener(null);
}
updateContentEndPaddings();
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 03a7b3da6251af9707cabeee7e8eabf6b8d422c0..93690cdfc811d7ee64734291d932634b47b1573a 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1430,6 +1430,32 @@ public class LockPatternUtils {
== StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
}
+ private static class WrappedCallback extends ICheckCredentialProgressCallback.Stub {
+
+ private Handler mHandler;
+ private CheckCredentialProgressCallback mCallback;
+
+ WrappedCallback(Handler handler, CheckCredentialProgressCallback callback) {
+ mHandler = handler;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onCredentialVerified() throws RemoteException {
+ if (mHandler == null) {
+ Log.e(TAG, "Handler is null during callback");
+ }
+ // Kill reference immediately to allow early GC at client side independent of
+ // when system_server decides to lose its reference to the
+ // ICheckCredentialProgressCallback binder object.
+ mHandler.post(() -> {
+ mCallback.onEarlyMatched();
+ mCallback = null;
+ });
+ mHandler = null;
+ }
+ }
+
private ICheckCredentialProgressCallback wrapCallback(
final CheckCredentialProgressCallback callback) {
if (callback == null) {
@@ -1439,13 +1465,7 @@ public class LockPatternUtils {
throw new IllegalStateException("Must construct LockPatternUtils on a looper thread"
+ " to use progress callbacks.");
}
- return new ICheckCredentialProgressCallback.Stub() {
-
- @Override
- public void onCredentialVerified() throws RemoteException {
- mHandler.post(callback::onEarlyMatched);
- }
- };
+ return new WrappedCallback(mHandler, callback);
}
}
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 53272f7eebf95379ea59e859ab04103f765a1565..f312d1d4f25db353bbd32e7254482b213c00e6a2 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -42,6 +42,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
+import android.widget.TextView;
import com.android.internal.R;
@@ -109,6 +110,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
private ViewGroup mMessagingIconContainer;
private int mConversationContentStart;
private int mNonConversationMarginEnd;
+ private int mNotificationTextMarginTop;
public MessagingGroup(@NonNull Context context) {
super(context);
@@ -148,6 +150,8 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
R.dimen.conversation_content_start);
mNonConversationMarginEnd = getResources().getDimensionPixelSize(
R.dimen.messaging_layout_margin_end);
+ mNotificationTextMarginTop = getResources().getDimensionPixelSize(
+ R.dimen.notification_text_margin_top);
}
public void updateClipRect() {
@@ -612,7 +616,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
return 0;
}
- public View getSenderView() {
+ public TextView getSenderView() {
return mSenderView;
}
@@ -664,10 +668,14 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
public void setSingleLine(boolean singleLine) {
if (singleLine != mSingleLine) {
mSingleLine = singleLine;
+ MarginLayoutParams p = (MarginLayoutParams) mMessageContainer.getLayoutParams();
+ p.topMargin = singleLine ? 0 : mNotificationTextMarginTop;
+ mMessageContainer.setLayoutParams(p);
mContentContainer.setOrientation(
singleLine ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
MarginLayoutParams layoutParams = (MarginLayoutParams) mSenderView.getLayoutParams();
layoutParams.setMarginEnd(singleLine ? mSenderTextPaddingSingleLine : 0);
+ mSenderView.setSingleLine(singleLine);
updateMaxDisplayedLines();
updateClipRect();
updateSenderVisibility();
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index a162e4e10c71f3157caa3d310a5b4746b7c6fe4f..27cd6e13d86c00c26ce9ef280e25515cda11e13a 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -124,7 +124,6 @@ public class MessagingLayout extends FrameLayout
protected void onFinishInflate() {
super.onFinishInflate();
mMessagingLinearLayout = findViewById(R.id.notification_messaging);
- mMessagingLinearLayout.setMessagingLayout(this);
// We still want to clip, but only on the top, since views can temporarily out of bounds
// during transitions.
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index ac04862d9a7d7f1680bfdff72c9a3bf7158ba760..7cfd46c880fce665175da7b9d4345c688b23a4ba 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -24,6 +24,7 @@ import android.util.AttributeSet;
import android.view.RemotableViewMethod;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.widget.RemoteViews;
import com.android.internal.R;
@@ -43,8 +44,6 @@ public class MessagingLinearLayout extends ViewGroup {
private int mMaxDisplayedLines = Integer.MAX_VALUE;
- private IMessagingLayout mMessagingLayout;
-
public MessagingLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -292,12 +291,40 @@ public class MessagingLinearLayout extends ViewGroup {
mMaxDisplayedLines = numberLines;
}
- public void setMessagingLayout(IMessagingLayout layout) {
- mMessagingLayout = layout;
+ public IMessagingLayout getMessagingLayout() {
+ View view = this;
+ while (true) {
+ ViewParent p = view.getParent();
+ if (p instanceof View) {
+ view = (View) p;
+ if (view instanceof IMessagingLayout) {
+ return (IMessagingLayout) view;
+ }
+ } else {
+ return null;
+ }
+ }
}
- public IMessagingLayout getMessagingLayout() {
- return mMessagingLayout;
+ @Override
+ public int getBaseline() {
+ // When placed in a horizontal linear layout (as is the case in a single-line MessageGroup),
+ // align with the last visible child (which is the one that will be displayed in the single-
+ // line group.
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ final View child = getChildAt(i);
+ if (isGone(child)) {
+ continue;
+ }
+ final int childBaseline = child.getBaseline();
+ if (childBaseline == -1) {
+ return -1;
+ }
+ MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+ return lp.topMargin + childBaseline;
+ }
+ return super.getBaseline();
}
public interface MessagingChild {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 21ca948fa89cbc1ffd50b314a04c764bea8f4928..ea390cd71e3186d1cd67e10a535e29ae26ccd72a 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -24,6 +24,7 @@ import android.content.ComponentName;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.CarrierAssociatedAppEntry;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Process;
@@ -198,8 +199,8 @@ public class SystemConfig {
// These are the packages of carrier-associated apps which should be disabled until used until
// a SIM is inserted which grants carrier privileges to that carrier app.
- final ArrayMap> mDisabledUntilUsedPreinstalledCarrierAssociatedApps =
- new ArrayMap<>();
+ final ArrayMap>
+ mDisabledUntilUsedPreinstalledCarrierAssociatedApps = new ArrayMap<>();
final ArrayMap> mPrivAppPermissions = new ArrayMap<>();
final ArrayMap> mPrivAppDenyPermissions = new ArrayMap<>();
@@ -331,7 +332,8 @@ public class SystemConfig {
return mDisabledUntilUsedPreinstalledCarrierApps;
}
- public ArrayMap> getDisabledUntilUsedPreinstalledCarrierAssociatedApps() {
+ public ArrayMap>
+ getDisabledUntilUsedPreinstalledCarrierAssociatedApps() {
return mDisabledUntilUsedPreinstalledCarrierAssociatedApps;
}
@@ -954,7 +956,23 @@ public class SystemConfig {
+ "> without package or carrierAppPackage in " + permFile
+ " at " + parser.getPositionDescription());
} else {
- List associatedPkgs =
+ // APKs added to system images via OTA should specify the addedInSdk
+ // attribute, otherwise they may be enabled-by-default in too many
+ // cases. See CarrierAppUtils for more info.
+ int addedInSdk = CarrierAssociatedAppEntry.SDK_UNSPECIFIED;
+ String addedInSdkStr = parser.getAttributeValue(null, "addedInSdk");
+ if (!TextUtils.isEmpty(addedInSdkStr)) {
+ try {
+ addedInSdk = Integer.parseInt(addedInSdkStr);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "<" + name + "> addedInSdk not an integer in "
+ + permFile + " at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ break;
+ }
+ }
+ List associatedPkgs =
mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
carrierPkgname);
if (associatedPkgs == null) {
@@ -962,7 +980,8 @@ public class SystemConfig {
mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
carrierPkgname, associatedPkgs);
}
- associatedPkgs.add(pkgname);
+ associatedPkgs.add(
+ new CarrierAssociatedAppEntry(pkgname, addedInSdk));
}
} else {
logNotAllowedInPartition(name, permFile, parser);
@@ -1197,6 +1216,10 @@ public class SystemConfig {
addFeature(PackageManager.FEATURE_APP_ENUMERATION, 0);
}
+ if (Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.Q) {
+ addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
+ }
+
for (String featureName : mUnavailableFeatures) {
removeFeature(featureName);
}
diff --git a/core/jni/android_media_AudioEffectDescriptor.cpp b/core/jni/android_media_AudioEffectDescriptor.cpp
index 37d8114052b8ad4bf2072889d715821115086a0f..1435e879053cdcfd863266dde174c8e887617401 100644
--- a/core/jni/android_media_AudioEffectDescriptor.cpp
+++ b/core/jni/android_media_AudioEffectDescriptor.cpp
@@ -102,9 +102,9 @@ void convertAudioEffectDescriptorVectorFromNative(JNIEnv *env, jobjectArray *jDe
*jDescriptors = env->NewObjectArray(actualSize, audioEffectDescriptorClass(), NULL);
for (size_t i = 0; i < actualSize; i++) {
- env->SetObjectArrayElement(*jDescriptors,
- i,
- env->GetObjectArrayElement(temp, i));
+ jobject jdesc = env->GetObjectArrayElement(temp, i);
+ env->SetObjectArrayElement(*jDescriptors, i, jdesc);
+ env->DeleteLocalRef(jdesc);
}
env->DeleteLocalRef(temp);
}
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index a3c455bfc111d0f171b0123ad811034b30443212..b1b39f3e36ffe1f40a24bfd176b46a8759fc99f0 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -47,6 +47,7 @@
#define CHANNEL_INVALID 0
#define CHANNEL_OUT_DEFAULT 1
+#define CHANNEL_IN_DEFAULT 1
static inline audio_format_t audioFormatToNative(int audioFormat)
{
@@ -196,12 +197,22 @@ static inline int outChannelMaskFromNative(audio_channel_mask_t nativeMask)
static inline audio_channel_mask_t inChannelMaskToNative(int channelMask)
{
- return (audio_channel_mask_t)channelMask;
+ switch (channelMask) {
+ case CHANNEL_IN_DEFAULT:
+ return AUDIO_CHANNEL_NONE;
+ default:
+ return (audio_channel_mask_t)channelMask;
+ }
}
static inline int inChannelMaskFromNative(audio_channel_mask_t nativeMask)
{
- return (int)nativeMask;
+ switch (nativeMask) {
+ case AUDIO_CHANNEL_NONE:
+ return CHANNEL_IN_DEFAULT;
+ default:
+ return (int)nativeMask;
+ }
}
#endif // ANDROID_MEDIA_AUDIOFORMAT_H
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 03b9793ccba8be473cf255fb9ae450738a6c3493..d4805acb06d08b7e5c3c65a4bbd79b9b36910729 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -226,6 +226,11 @@ static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass);
}
+static void android_net_utils_setAllowNetworkingForProcess(JNIEnv *env, jobject thiz,
+ jboolean hasConnectivity) {
+ setAllowNetworkingForProcess(hasConnectivity == JNI_TRUE);
+}
+
static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
if (javaFd == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -266,6 +271,7 @@ static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, j
/*
* JNI registration.
*/
+// clang-format off
static const JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
@@ -282,7 +288,9 @@ static const JNINativeMethod gNetworkUtilMethods[] = {
{ "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
{ "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
{ "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork },
+ { "setAllowNetworkingForProcess", "(Z)V", (void *)android_net_utils_setAllowNetworkingForProcess },
};
+// clang-format on
int register_android_net_NetworkUtils(JNIEnv* env)
{
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index e553a786da96bfab242f7a2769d5b16281e06af2..ae36f8a7b30bf27025ab92599f8abae5d71a91bb 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -395,6 +395,16 @@ static void nativeSetEarlyWakeup(JNIEnv* env, jclass clazz, jlong transactionObj
transaction->setEarlyWakeup();
}
+static void nativeSetEarlyWakeupStart(JNIEnv* env, jclass clazz, jlong transactionObj) {
+ auto transaction = reinterpret_cast(transactionObj);
+ transaction->setExplicitEarlyWakeupStart();
+}
+
+static void nativeSetEarlyWakeupEnd(JNIEnv* env, jclass clazz, jlong transactionObj) {
+ auto transaction = reinterpret_cast(transactionObj);
+ transaction->setExplicitEarlyWakeupEnd();
+}
+
static void nativeSetLayer(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint zorder) {
auto transaction = reinterpret_cast(transactionObj);
@@ -1501,6 +1511,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetAnimationTransaction },
{"nativeSetEarlyWakeup", "(J)V",
(void*)nativeSetEarlyWakeup },
+ {"nativeSetEarlyWakeupStart", "(J)V",
+ (void*)nativeSetEarlyWakeupStart },
+ {"nativeSetEarlyWakeupEnd", "(J)V",
+ (void*)nativeSetEarlyWakeupEnd },
{"nativeSetLayer", "(JJI)V",
(void*)nativeSetLayer },
{"nativeSetRelativeLayer", "(JJJI)V",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index fc2005a31696cc3f4a3368a992f5288f03816bab..9eede83e21e52b265e80460954a1223f9915dcaf 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -526,8 +526,16 @@ static void UnsetChldSignalHandler() {
// Calls POSIX setgroups() using the int[] object as an argument.
// A nullptr argument is tolerated.
-static void SetGids(JNIEnv* env, jintArray managed_gids, fail_fn_t fail_fn) {
+static void SetGids(JNIEnv* env, jintArray managed_gids, jboolean is_child_zygote,
+ fail_fn_t fail_fn) {
if (managed_gids == nullptr) {
+ if (is_child_zygote) {
+ // For child zygotes like webview and app zygote, we want to clear out
+ // any supplemental groups the parent zygote had.
+ if (setgroups(0, NULL) == -1) {
+ fail_fn(CREATE_ERROR("Failed to remove supplementary groups for child zygote"));
+ }
+ }
return;
}
@@ -1351,7 +1359,13 @@ static void isolateAppData(JNIEnv* env, const std::vector& merged_d
}
closedir(dir);
- bool legacySymlinkCreated = false;
+ // Prepare default dirs for user 0 as user 0 always exists.
+ int result = symlink("/data/data", "/data/user/0");
+ if (result != 0) {
+ fail_fn(CREATE_ERROR("Failed to create symlink /data/user/0 %s", strerror(errno)));
+ }
+ PrepareDirIfNotPresent("/data/user_de/0", DEFAULT_DATA_DIR_PERMISSION,
+ AID_ROOT, AID_ROOT, fail_fn);
for (int i = 0; i < size; i += 3) {
std::string const & packageName = merged_data_info_list[i];
@@ -1392,17 +1406,8 @@ static void isolateAppData(JNIEnv* env, const std::vector& merged_d
char internalDeUserPath[PATH_MAX];
snprintf(internalCeUserPath, PATH_MAX, "/data/user/%d", userId);
snprintf(internalDeUserPath, PATH_MAX, "/data/user_de/%d", userId);
- // If it's user 0, create a symlink /data/user/0 -> /data/data,
- // otherwise create /data/user/$USER
+ // If it's not user 0, create /data/user/$USER.
if (userId == 0) {
- if (!legacySymlinkCreated) {
- legacySymlinkCreated = true;
- int result = symlink(internalLegacyCePath, internalCeUserPath);
- if (result != 0) {
- fail_fn(CREATE_ERROR("Failed to create symlink %s %s", internalCeUserPath,
- strerror(errno)));
- }
- }
actualCePath = internalLegacyCePath;
} else {
PrepareDirIfNotPresent(internalCeUserPath, DEFAULT_DATA_DIR_PERMISSION,
@@ -1579,10 +1584,6 @@ static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list,
// Fuse is ready, so we can start using fuse path.
int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
- if (size == 0) {
- fail_fn(CREATE_ERROR("Data package list cannot be empty"));
- }
-
// Create tmpfs on Android/obb and Android/data so these 2 dirs won't enter fuse anymore.
std::string androidObbDir = StringPrintf("/storage/emulated/%d/Android/obb", user_id);
MountAppDataTmpFs(androidObbDir, fail_fn);
@@ -1665,7 +1666,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
}
}
- SetGids(env, gids, fail_fn);
+ SetGids(env, gids, is_child_zygote, fail_fn);
SetRLimits(env, rlimits, fail_fn);
if (need_pre_initialize_native_bridge) {
@@ -1736,6 +1737,8 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
}
android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level));
+ // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
+ runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
bool forceEnableGwpAsan = false;
switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
@@ -1748,6 +1751,8 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY:
android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan));
}
+ // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
+ runtime_flags &= ~RuntimeFlags::GWP_ASAN_LEVEL_MASK;
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto
index 563ef145b79c87331ea6b62ccf923e20ba2a6468..bd5cb62f7fdecb206585d51f297942835c6e3294 100644
--- a/core/proto/android/app/enums.proto
+++ b/core/proto/android/app/enums.proto
@@ -206,4 +206,5 @@ enum AppOpEnum {
APP_OP_DEPRECATED_1 = 96 [deprecated = true];
APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97;
APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98;
+ APP_OP_NO_ISOLATED_STORAGE = 99;
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 997829eacf96c66df8c3e36b1b4bd05fe76976ec..69b32c264d3df3c9ccebed3a9fb4f65e02c28f8b 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2678,4 +2678,9 @@ enum PageId {
// CATEGORY: SETTINGS
// OS: R
DEVICE_CONTROLS_SETTINGS = 1844;
+
+ // OPEN: Settings > Sound > Media
+ // CATEGORY: SETTINGS
+ // OS: R
+ MEDIA_CONTROLS_SETTINGS = 1845;
}
diff --git a/core/proto/android/server/blobstoremanagerservice.proto b/core/proto/android/server/blobstoremanagerservice.proto
new file mode 100644
index 0000000000000000000000000000000000000000..583b646eb9c7884def2c99171841b6bd82659744
--- /dev/null
+++ b/core/proto/android/server/blobstoremanagerservice.proto
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.server.blob;
+
+option java_multiple_files = true;
+
+// The nested messages are used for statsd logging and should be kept in sync with the messages
+// of the same name in frameworks/base/cmds/statsd/src/atoms.proto
+message BlobStatsEventProto {
+ // Blob Committer stats
+ // Keep in sync between:
+ // frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+ // frameworks/base/cmds/statsd/src/atoms.proto
+ message BlobCommitterProto {
+ // Committer app's uid
+ optional int32 uid = 1;
+
+ // Unix epoch timestamp of the commit in milliseconds
+ optional int64 commit_timestamp_millis = 2;
+
+ // Flags of what access types the committer has set for the Blob
+ optional int32 access_mode = 3;
+
+ // Number of packages that have been whitelisted for ACCESS_TYPE_WHITELIST
+ optional int32 num_whitelisted_package = 4;
+ }
+
+ // Blob Leasee stats
+ // Keep in sync between:
+ // frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+ // frameworks/base/cmds/statsd/src/atoms.proto
+ message BlobLeaseeProto {
+ // Leasee app's uid
+ optional int32 uid = 1;
+
+ // Unix epoch timestamp for lease expiration in milliseconds
+ optional int64 lease_expiry_timestamp_millis = 2;
+ }
+
+ // List of Blob Committers
+ // Keep in sync between:
+ // frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+ // frameworks/base/cmds/statsd/src/atoms.proto
+ message BlobCommitterListProto {
+ repeated BlobCommitterProto committer = 1;
+ }
+
+ // List of Blob Leasees
+ // Keep in sync between:
+ // frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
+ // frameworks/base/cmds/statsd/src/atoms.proto
+ message BlobLeaseeListProto {
+ repeated BlobLeaseeProto leasee = 1;
+ }
+}
\ No newline at end of file
diff --git a/core/proto/android/server/connectivity/data_stall_event.proto b/core/proto/android/server/connectivity/data_stall_event.proto
index 23fcf6ebc2cc0b819be006861d0ac1088b70b1ca..787074ba494eb1fbda0e82ba3ddeed4eea1c1b69 100644
--- a/core/proto/android/server/connectivity/data_stall_event.proto
+++ b/core/proto/android/server/connectivity/data_stall_event.proto
@@ -32,6 +32,7 @@ enum ApBand {
AP_BAND_UNKNOWN = 0;
AP_BAND_2GHZ = 1;
AP_BAND_5GHZ = 2;
+ AP_BAND_6GHZ = 3;
}
// Refer to definition in TelephonyManager.java.
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index ec99684bf6366ccaa2f3bc0851b184589c9cb5d3..f2f20e3ac12ebf646821b41c586b40a408a0f595 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -236,6 +236,8 @@ message ConstantsProto {
optional int64 api_quota_schedule_window_ms = 33;
// Whether or not to throw an exception when an app hits its schedule quota limit.
optional bool api_quota_schedule_throw_exception = 34;
+ // Whether or not to return a failure result when an app hits its schedule quota limit.
+ optional bool api_quota_schedule_return_failure_result = 35;
message QuotaController {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -335,7 +337,7 @@ message ConstantsProto {
// In this time after screen turns on, we increase job concurrency.
optional int32 screen_off_job_concurrency_increase_delay_ms = 28;
- // Next tag: 35
+ // Next tag: 36
}
// Next tag: 4
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index ecb4193a2c6c1eb5c2fc39b522bb1955db748ad1..8e4006aa6861498ce70a069b31e1b19ad9133b2e 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -274,4 +274,74 @@ message PackageRemoteViewInfoProto {
// Next Tag: 2
message NotificationRemoteViewsProto {
repeated PackageRemoteViewInfoProto package_remote_view_info = 1;
-}
\ No newline at end of file
+}
+
+/**
+ * Atom that represents an item in the list of Do Not Disturb rules, pulled from
+ * NotificationManagerService.java.
+ */
+message DNDModeProto {
+ enum Mode {
+ ROOT_CONFIG = -1; // Used to distinguish the config (one per user) from the rules.
+ ZEN_MODE_OFF = 0;
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
+ ZEN_MODE_NO_INTERRUPTIONS = 2;
+ ZEN_MODE_ALARMS = 3;
+ }
+ optional int32 user = 1; // Android user ID (0, 1, 10, ...)
+ optional bool enabled = 2; // true for ROOT_CONFIG if a manualRule is enabled
+ optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG
+ optional Mode zen_mode = 4;
+ // id is one of the system default rule IDs, or empty
+ // May also be "MANUAL_RULE" to indicate app-activation of the manual rule.
+ optional string id = 5;
+ optional int32 uid = 6; // currently only SYSTEM_UID or 0 for other
+ optional DNDPolicyProto policy = 7;
+}
+
+/**
+ * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto.
+ */
+message DNDPolicyProto {
+ enum State {
+ STATE_UNSET = 0;
+ STATE_ALLOW = 1;
+ STATE_DISALLOW = 2;
+ }
+ optional State calls = 1;
+ optional State repeat_callers = 2;
+ optional State messages = 3;
+ optional State conversations = 4;
+ optional State reminders = 5;
+ optional State events = 6;
+ optional State alarms = 7;
+ optional State media = 8;
+ optional State system = 9;
+ optional State fullscreen = 10;
+ optional State lights = 11;
+ optional State peek = 12;
+ optional State status_bar = 13;
+ optional State badge = 14;
+ optional State ambient = 15;
+ optional State notification_list = 16;
+
+ enum PeopleType {
+ PEOPLE_UNSET = 0;
+ PEOPLE_ANYONE = 1;
+ PEOPLE_CONTACTS = 2;
+ PEOPLE_STARRED = 3;
+ PEOPLE_NONE = 4;
+ }
+
+ optional PeopleType allow_calls_from = 17;
+ optional PeopleType allow_messages_from = 18;
+
+ enum ConversationType {
+ CONV_UNSET = 0;
+ CONV_ANYONE = 1;
+ CONV_IMPORTANT = 2;
+ CONV_NONE = 3;
+ }
+
+ optional ConversationType allow_conversations_from = 19;
+}
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index dd830a85edc9407cead3ded89f656054f8324456..7a4c0706e1190a9173f07a62375b7f21e96f390e 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -179,13 +179,16 @@ message ProcessStatsProto {
repeated ProcessStatsAssociationProto assocs = 7;
}
-// Next Tag: 5
+// Next Tag: 6
message ProcessStatsAssociationProto {
// Procss Name of the associated process/package
optional string assoc_process_name = 1;
// Package Name of the associated process/package
- optional string assoc_package_name = 2;
+ optional string assoc_package_name = 2 [deprecated = true];
+
+ // UID of the associated process/package
+ optional int32 assoc_uid = 5;
// Total count of the times this association appeared.
optional int32 total_count = 3;
diff --git a/core/proto/android/stats/connectivity/Android.bp b/core/proto/android/stats/connectivity/Android.bp
index 5d642d3845fee472b3b3e651f31a4a97dcfe0a37..5e6ac3cd3ca1ab61d801693757f85690050133e5 100644
--- a/core/proto/android/stats/connectivity/Android.bp
+++ b/core/proto/android/stats/connectivity/Android.bp
@@ -13,12 +13,26 @@
// limitations under the License.
java_library_static {
- name: "networkstackprotosnano",
+ name: "networkstackprotos",
proto: {
- type: "nano",
+ type: "lite",
},
srcs: [
"network_stack.proto",
],
+ sdk_version: "system_29",
+}
+
+java_library_static {
+ name: "tetheringprotos",
+ proto: {
+ type: "lite",
+ },
+ srcs: [
+ "tethering.proto",
+ ],
+ apex_available: [
+ "com.android.tethering",
+ ],
sdk_version: "system_current",
}
diff --git a/core/proto/android/stats/connectivity/network_stack.proto b/core/proto/android/stats/connectivity/network_stack.proto
index 7d9aa1c6eb230ab293eb7cea7af81a1c6a3619b3..e9726d7ce195426cec8723d10ed2575dd46d5999 100644
--- a/core/proto/android/stats/connectivity/network_stack.proto
+++ b/core/proto/android/stats/connectivity/network_stack.proto
@@ -20,6 +20,160 @@ package android.stats.connectivity;
option java_multiple_files = true;
option java_outer_classname = "NetworkStackProto";
+enum DhcpRenewResult {
+ RR_UNKNOWN = 0;
+ RR_SUCCESS = 1;
+ RR_ERROR_NAK = 2;
+ RR_ERROR_IP_MISMATCH = 3;
+ RR_ERROR_IP_EXPIRE = 4;
+}
+
+enum DisconnectCode {
+ DC_NONE = 0;
+ DC_NORMAL_TERMINATION = 1;
+ DC_PROVISIONING_FAIL = 2;
+ DC_ERROR_STARTING_IPV4 = 4;
+ DC_ERROR_STARTING_IPV6 = 5;
+ DC_ERROR_STARTING_IPREACHABILITYMONITOR = 6;
+ DC_INVALID_PROVISIONING = 7;
+ DC_INTERFACE_NOT_FOUND = 8;
+ DC_PROVISIONING_TIMEOUT = 9;
+}
+
+enum TransportType {
+ TT_UNKNOWN = 0;
+ // Indicates this network uses a Cellular transport
+ TT_CELLULAR = 1;
+ // Indicates this network uses a Wi-Fi transport
+ TT_WIFI = 2;
+ // Indicates this network uses a Bluetooth transport
+ TT_BLUETOOTH = 3;
+ // Indicates this network uses an Ethernet transport
+ TT_ETHERNET = 4;
+ // Indicates this network uses a Wi-Fi Aware transport
+ TT_WIFI_AWARE = 5;
+ // Indicates this network uses a LoWPAN transport
+ TT_LOWPAN = 6;
+ // Indicates this network uses a Cellular+VPN transport
+ TT_CELLULAR_VPN = 7;
+ // Indicates this network uses a Wi-Fi+VPN transport
+ TT_WIFI_VPN = 8;
+ // Indicates this network uses a Bluetooth+VPN transport
+ TT_BLUETOOTH_VPN = 9;
+ // Indicates this network uses an Ethernet+VPN transport
+ TT_ETHERNET_VPN = 10;
+ // Indicates this network uses a Wi-Fi+Cellular+VPN transport
+ TT_WIFI_CELLULAR_VPN = 11;
+ // Indicates this network uses for test only
+ TT_TEST = 12;
+}
+
+enum DhcpFeature {
+ DF_UNKNOWN = 0;
+ // DHCP INIT-REBOOT state
+ DF_INITREBOOT = 1;
+ // DHCP rapid commit option
+ DF_RAPIDCOMMIT = 2;
+ // Duplicate address detection
+ DF_DAD = 3;
+ // Fast initial Link setup
+ DF_FILS = 4;
+}
+
+enum HostnameTransResult {
+ HTR_UNKNOWN = 0;
+ HTR_SUCCESS = 1;
+ HTR_FAILURE = 2;
+ HTR_DISABLE = 3;
+}
+
+enum ProbeResult {
+ PR_UNKNOWN = 0;
+ PR_SUCCESS = 1;
+ PR_FAILURE = 2;
+ PR_PORTAL = 3;
+ // DNS query for the probe host returned a private IP address
+ PR_PRIVATE_IP_DNS = 4;
+}
+
+enum ValidationResult {
+ VR_UNKNOWN = 0;
+ VR_SUCCESS = 1;
+ VR_FAILURE = 2;
+ VR_PORTAL = 3;
+ VR_PARTIAL = 4;
+}
+
+enum ProbeType {
+ PT_UNKNOWN = 0;
+ PT_DNS = 1;
+ PT_HTTP = 2;
+ PT_HTTPS = 3;
+ PT_PAC = 4;
+ PT_FALLBACK = 5;
+ PT_PRIVDNS = 6;
+ PT_CAPPORT_API = 7;
+}
+
+// The Dhcp error code is defined in android.net.metrics.DhcpErrorEvent
+enum DhcpErrorCode {
+ ET_UNKNOWN = 0;
+ ET_L2_ERROR = 1;
+ ET_L3_ERROR = 2;
+ ET_L4_ERROR = 3;
+ ET_DHCP_ERROR = 4;
+ ET_MISC_ERROR = 5;
+ /* Reserve for error type
+ // ET_L2_ERROR_TYPE = ET_L2_ERROR << 8;
+ ET_L2_ERROR_TYPE = 256;
+ // ET_L3_ERROR_TYPE = ET_L3_ERROR << 8;
+ ET_L3_ERROR_TYPE = 512;
+ // ET_L4_ERROR_TYPE = ET_L4_ERROR << 8;
+ ET_L4_ERROR_TYPE = 768;
+ // ET_DHCP_ERROR_TYPE = ET_DHCP_ERROR << 8;
+ ET_DHCP_ERROR_TYPE = 1024;
+ // ET_MISC_ERROR_TYPE = ET_MISC_ERROR << 8;
+ ET_MISC_ERROR_TYPE = 1280;
+ */
+ // ET_L2_TOO_SHORT = (ET_L2_ERROR_TYPE | 0x1) << 16;
+ ET_L2_TOO_SHORT = 16842752;
+ // ET_L2_WRONG_ETH_TYPE = (ET_L2_ERROR_TYPE | 0x2) << 16;
+ ET_L2_WRONG_ETH_TYPE = 16908288;
+ // ET_L3_TOO_SHORT = (ET_L3_ERROR_TYPE | 0x1) << 16;
+ ET_L3_TOO_SHORT = 33619968;
+ // ET_L3_NOT_IPV4 = (ET_L3_ERROR_TYPE | 0x2) << 16;
+ ET_L3_NOT_IPV4 = 33685504;
+ // ET_L3_INVALID_IP = (ET_L3_ERROR_TYPE | 0x3) << 16;
+ ET_L3_INVALID_IP = 33751040;
+ // ET_L4_NOT_UDP = (ET_L4_ERROR_TYPE | 0x1) << 16;
+ ET_L4_NOT_UDP = 50397184;
+ // ET_L4_WRONG_PORT = (ET_L4_ERROR_TYPE | 0x2) << 16;
+ ET_L4_WRONG_PORT = 50462720;
+ // ET_BOOTP_TOO_SHORT = (ET_DHCP_ERROR_TYPE | 0x1) << 16;
+ ET_BOOTP_TOO_SHORT = 67174400;
+ // ET_DHCP_BAD_MAGIC_COOKIE = (ET_DHCP_ERROR_TYPE | 0x2) << 16;
+ ET_DHCP_BAD_MAGIC_COOKIE = 67239936;
+ // ET_DHCP_INVALID_OPTION_LENGTH = (ET_DHCP_ERROR_TYPE | 0x3) << 16;
+ ET_DHCP_INVALID_OPTION_LENGTH = 67305472;
+ // ET_DHCP_NO_MSG_TYPE = (ET_DHCP_ERROR_TYPE | 0x4) << 16;
+ ET_DHCP_NO_MSG_TYPE = 67371008;
+ // ET_DHCP_UNKNOWN_MSG_TYPE = (ET_DHCP_ERROR_TYPE | 0x5) << 16;
+ ET_DHCP_UNKNOWN_MSG_TYPE = 67436544;
+ // ET_DHCP_NO_COOKIE = (ET_DHCP_ERROR_TYPE | 0x6) << 16;
+ ET_DHCP_NO_COOKIE = 67502080;
+ // ET_BUFFER_UNDERFLOW = (ET_MISC_ERROR_TYPE | 0x1) << 16;
+ ET_BUFFER_UNDERFLOW = 83951616;
+ // ET_RECEIVE_ERROR = (ET_MISC_ERROR_TYPE | 0x2) << 16;
+ ET_RECEIVE_ERROR = 84017152;
+ // ET_PARSING_ERROR = (ET_MISC_ERROR_TYPE | 0x3) << 16;
+ ET_PARSING_ERROR = 84082688;
+}
+
+enum NetworkQuirkEvent {
+ QE_UNKNOWN = 0;
+ QE_IPV6_PROVISIONING_ROUTER_LOST = 1;
+}
+
message NetworkStackEventData {
}
diff --git a/core/proto/android/stats/connectivity/tethering.proto b/core/proto/android/stats/connectivity/tethering.proto
new file mode 100644
index 0000000000000000000000000000000000000000..13f0b8c44fb52ba2ec90bed830ebd2c847629871
--- /dev/null
+++ b/core/proto/android/stats/connectivity/tethering.proto
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+package android.stats.connectivity;
+option java_multiple_files = true;
+option java_outer_classname = "TetheringProto";
+
+enum ErrorCode {
+ EC_NO_ERROR = 0;
+ EC_UNKNOWN_IFACE = 1;
+ EC_SERVICE_UNAVAIL = 2;
+ EC_UNSUPPORTED = 3;
+ EC_UNAVAIL_IFACE = 4;
+ EC_INTERNAL_ERROR = 5;
+ EC_TETHER_IFACE_ERROR = 6;
+ EC_UNTETHER_IFACE_ERROR = 7;
+ EC_ENABLE_FORWARDING_ERROR = 8;
+ EC_DISABLE_FORWARDING_ERROR = 9;
+ EC_IFACE_CFG_ERROR = 10;
+ EC_PROVISIONING_FAILED = 11;
+ EC_DHCPSERVER_ERROR = 12;
+ EC_ENTITLEMENT_UNKNOWN = 13;
+ EC_NO_CHANGE_TETHERING_PERMISSION = 14;
+ EC_NO_ACCESS_TETHERING_PERMISSION = 15;
+ EC_UNKNOWN_TYPE = 16;
+}
+
+enum DownstreamType {
+ // Unspecific tethering type.
+ DS_UNSPECIFIED = 0;
+ // Wifi tethering type.
+ DS_TETHERING_WIFI = 1;
+ // USB tethering type.
+ DS_TETHERING_USB = 2;
+ // Bluetooth tethering type.
+ DS_TETHERING_BLUETOOTH = 3;
+ // Wifi P2p tethering type.
+ DS_TETHERING_WIFI_P2P = 4;
+ // NCM (Network Control Model) local tethering type.
+ DS_TETHERING_NCM = 5;
+ // Ethernet tethering type.
+ DS_TETHERING_ETHERNET = 6;
+}
+
+enum UpstreamType {
+ UT_UNKNOWN = 0;
+ // Indicates upstream using a Cellular transport.
+ UT_CELLULAR = 1;
+ // Indicates upstream using a Wi-Fi transport.
+ UT_WIFI = 2;
+ // Indicates upstream using a Bluetooth transport.
+ UT_BLUETOOTH = 3;
+ // Indicates upstream using an Ethernet transport.
+ UT_ETHERNET = 4;
+ // Indicates upstream using a Wi-Fi Aware transport.
+ UT_WIFI_AWARE = 5;
+ // Indicates upstream using a LoWPAN transport.
+ UT_LOWPAN = 6;
+ // Indicates upstream using a Cellular+VPN transport.
+ UT_CELLULAR_VPN = 7;
+ // Indicates upstream using a Wi-Fi+VPN transport.
+ UT_WIFI_VPN = 8;
+ // Indicates upstream using a Bluetooth+VPN transport.
+ UT_BLUETOOTH_VPN = 9;
+ // Indicates upstream using an Ethernet+VPN transport.
+ UT_ETHERNET_VPN = 10;
+ // Indicates upstream using a Wi-Fi+Cellular+VPN transport.
+ UT_WIFI_CELLULAR_VPN = 11;
+ // Indicates upstream using for test only.
+ UT_TEST = 12;
+ // Indicates upstream using DUN capability + Cellular transport.
+ UT_DUN_CELLULAR = 13;
+}
+
+enum UserType {
+ // Unknown.
+ USER_UNKNOWN = 0;
+ // Settings.
+ USER_SETTINGS = 1;
+ // System UI.
+ USER_SYSTEMUI = 2;
+ // Google mobile service.
+ USER_GMS = 3;
+}
diff --git a/core/proto/android/stats/launcher/launcher.proto b/core/proto/android/stats/launcher/launcher.proto
index dbd0e038c40c1116b907b1718de3e31e21230daa..fc177d57b19367727da18ccd9c7f8cca4151f6d4 100644
--- a/core/proto/android/stats/launcher/launcher.proto
+++ b/core/proto/android/stats/launcher/launcher.proto
@@ -32,10 +32,12 @@ enum LauncherAction {
}
enum LauncherState {
- BACKGROUND = 0;
- HOME = 1;
- OVERVIEW = 2;
- ALLAPPS = 3;
+ LAUNCHER_STATE_UNSPECIFIED = 0;
+ BACKGROUND = 1;
+ HOME = 2;
+ OVERVIEW = 3;
+ ALLAPPS = 4;
+ UNCHANGED = 5;
}
message LauncherTarget {
diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto
index e1af9622adb326804832d6dc966abcbabca1f248..9f0ff591a5063e57a75b618b416458d351ef0b1e 100644
--- a/core/proto/android/stats/mediametrics/mediametrics.proto
+++ b/core/proto/android/stats/mediametrics/mediametrics.proto
@@ -131,7 +131,7 @@ message AudioTrackData {
* Logged from:
* frameworks/av/media/libstagefright/MediaCodec.cpp
* frameworks/av/services/mediaanalytics/statsd_codec.cpp
- * Next Tag: 21
+ * Next Tag: 26
*/
message CodecData {
optional string codec = 1;
@@ -156,6 +156,9 @@ message CodecData {
optional int64 latency_unknown = 20;
optional int32 queue_input_buffer_error = 21;
optional int32 queue_secure_input_buffer_error = 22;
+ optional string bitrate_mode = 23;
+ optional int32 bitrate = 24;
+ optional int64 lifetime_millis = 25;
}
/**
diff --git a/core/proto/android/stats/sysui/notification_enums.proto b/core/proto/android/stats/sysui/notification_enums.proto
index 09837022e50d31d03b3272c624399101ec3e5488..30bdecae07d142f2fcfc697cf1dcfab8e81c9cad 100644
--- a/core/proto/android/stats/sysui/notification_enums.proto
+++ b/core/proto/android/stats/sysui/notification_enums.proto
@@ -26,4 +26,5 @@ enum NotificationImportance { // Constants from NotificationManager.java
IMPORTANCE_LOW = 2; // Shows in shade, maybe status bar, no buzz/beep.
IMPORTANCE_DEFAULT = 3; // Shows everywhere, makes noise, no heads-up.
IMPORTANCE_HIGH = 4; // Shows everywhere, makes noise, heads-up, may full-screen.
+ IMPORTANCE_IMPORTANT_CONVERSATION = 5; // High + isImportantConversation().
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fd8460f9c4786683fd43c52087c834c8b1257dc6..9945057f0e941fcdb9fa61406c1690f28ca21f52 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3158,7 +3158,7 @@
@hide
-->
+ android:protectionLevel="signature|preinstalled" />
+
+
+ android:protectionLevel="signature|privileged|wellbeing|development" />
@@ -5025,6 +5028,10 @@
+
+
+
@@ -5227,7 +5234,7 @@
diff --git a/core/res/res/anim/screen_rotate_180_enter.xml b/core/res/res/anim/screen_rotate_180_enter.xml
index 889a615e07f486bcd409cf00cac517b86e5a1e17..3b6b4072dbcdecf7bf676fc93c285135251f1f26 100644
--- a/core/res/res/anim/screen_rotate_180_enter.xml
+++ b/core/res/res/anim/screen_rotate_180_enter.xml
@@ -25,4 +25,10 @@
android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/fast_out_slow_in"
android:duration="@android:integer/config_screen_rotation_total_180" />
+
diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml
index 766fcfae1f91b60bb6de13a6fdc714a3b3eb1d35..26fb6d8df506bd7e3ba6a7936aa75c5373edec9b 100644
--- a/core/res/res/anim/screen_rotate_180_exit.xml
+++ b/core/res/res/anim/screen_rotate_180_exit.xml
@@ -25,4 +25,9 @@
android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/fast_out_slow_in"
android:duration="@android:integer/config_screen_rotation_total_180" />
-
\ No newline at end of file
+
+
diff --git a/core/res/res/drawable-car-night/car_dialog_button_background.xml b/core/res/res/drawable-car-night/car_dialog_button_background.xml
new file mode 100644
index 0000000000000000000000000000000000000000..138cb38b0d874f73dee42ec43604258810949b64
--- /dev/null
+++ b/core/res/res/drawable-car-night/car_dialog_button_background.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/res/res/drawable-car/car_dialog_button_background.xml b/core/res/res/drawable-car/car_dialog_button_background.xml
index 67506cbc12bc8d098f20d134180e882b1312deaf..a7d40bcd759a21e7c46dd2f0b4332ac11bfdbeb1 100644
--- a/core/res/res/drawable-car/car_dialog_button_background.xml
+++ b/core/res/res/drawable-car/car_dialog_button_background.xml
@@ -14,9 +14,19 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-
-
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
diff --git a/core/res/res/drawable-nodpi/ic_number11.xml b/core/res/res/drawable-nodpi/ic_number11.xml
new file mode 100644
index 0000000000000000000000000000000000000000..daad61148c80e594ea7ababa0cb81fb506ef43db
--- /dev/null
+++ b/core/res/res/drawable-nodpi/ic_number11.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/core/res/res/drawable/chooser_action_button_bg.xml b/core/res/res/drawable/chooser_action_button_bg.xml
index a434c0b9b6a9b0b459e9195fdedafc2cdb7a7291..0dd9e9c7cd98b8d4f164c04aead64660873cf627 100644
--- a/core/res/res/drawable/chooser_action_button_bg.xml
+++ b/core/res/res/drawable/chooser_action_button_bg.xml
@@ -25,8 +25,8 @@
-
+ android:color="?attr/opacityListDivider" />
+
diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml b/core/res/res/drawable/chooser_dialog_background.xml
similarity index 64%
rename from packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
rename to core/res/res/drawable/chooser_dialog_background.xml
index 696e9b121564955710d39b95cc7bf8fa626b0552..b914d63187e703671eb3b24ff0c707411461e418 100644
--- a/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
+++ b/core/res/res/drawable/chooser_dialog_background.xml
@@ -1,7 +1,6 @@
-
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/core/res/res/drawable/conversation_badge_background.xml b/core/res/res/drawable/conversation_badge_background.xml
index 0dd0dcda40fb8ab9251713feded3d8088bf04820..9e6405dc104017995359265aa80b93ba19b81144 100644
--- a/core/res/res/drawable/conversation_badge_background.xml
+++ b/core/res/res/drawable/conversation_badge_background.xml
@@ -22,7 +22,7 @@
android:color="#ffffff"/>
+ android:width="20dp"
+ android:height="20dp"/>
diff --git a/core/res/res/drawable/conversation_badge_ring.xml b/core/res/res/drawable/conversation_badge_ring.xml
index 11ba8ad69505ee6ccf8f6453409822f94409bd2b..eee53d1c21b5d14ff9685b7469fc2b60d02d0deb 100644
--- a/core/res/res/drawable/conversation_badge_ring.xml
+++ b/core/res/res/drawable/conversation_badge_ring.xml
@@ -16,17 +16,18 @@
-->
-
-
+ android:shape="oval"
+>
+
+ android:width="@dimen/importance_ring_stroke_width"
+ />
+ android:width="@dimen/importance_ring_size"
+ android:height="@dimen/importance_ring_size"
+ />
diff --git a/core/res/res/layout-car/car_alert_dialog.xml b/core/res/res/layout-car/car_alert_dialog.xml
new file mode 100644
index 0000000000000000000000000000000000000000..569e5948e2e08aa78c43b1b6679c518cc5d05140
--- /dev/null
+++ b/core/res/res/layout-car/car_alert_dialog.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/res/res/layout-car/car_alert_dialog_button_bar.xml b/core/res/res/layout-car/car_alert_dialog_button_bar.xml
new file mode 100644
index 0000000000000000000000000000000000000000..277b0dcca657749689f1dbbbfcf50af9c9aae87a
--- /dev/null
+++ b/core/res/res/layout-car/car_alert_dialog_button_bar.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/res/res/layout-car/car_alert_dialog_title.xml b/core/res/res/layout-car/car_alert_dialog_title.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ba735a649dc2ea050f2223d481228826ff009ba5
--- /dev/null
+++ b/core/res/res/layout-car/car_alert_dialog_title.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/res/res/layout/chooser_action_button.xml b/core/res/res/layout/chooser_action_button.xml
index 119b2e90292d6da5722d3ec30b9fdca8e81d7e3d..6af7937960f0c48eb1a3858126038fe6029dfe35 100644
--- a/core/res/res/layout/chooser_action_button.xml
+++ b/core/res/res/layout/chooser_action_button.xml
@@ -19,12 +19,12 @@
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:drawablePadding="8dp"
- android:textColor="?android:textColorSecondary"
+ android:textColor="?android:textColorPrimary"
android:textSize="12sp"
android:maxWidth="192dp"
android:singleLine="true"
android:clickable="true"
android:background="@drawable/chooser_action_button_bg"
- android:drawableTint="?android:attr/colorControlNormal"
+ android:drawableTint="@color/chooser_chip_icon"
android:drawableTintMode="src_in"
/>
diff --git a/core/res/res/layout/chooser_dialog.xml b/core/res/res/layout/chooser_dialog.xml
new file mode 100644
index 0000000000000000000000000000000000000000..824136cd970aaac9016f36e2ecba0e43cf0f3a0f
--- /dev/null
+++ b/core/res/res/layout/chooser_dialog.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/res/res/layout/chooser_dialog_item.xml b/core/res/res/layout/chooser_dialog_item.xml
index 1d6369793bfbcad5d5343eb7982a8ed2f1212a86..4a88bb02372e390d39c1516dae84318eec18ac7c 100644
--- a/core/res/res/layout/chooser_dialog_item.xml
+++ b/core/res/res/layout/chooser_dialog_item.xml
@@ -16,27 +16,28 @@
-->
+ android:layout_height="wrap_content">
-
+ android:alpha="0.54"
+ android:tint="?android:attr/textColorPrimary"
+ android:layout_marginEnd="16dp"
+ android:layout_width="24dp"
+ android:layout_height="24dp"/>
-
diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml
index 002917463ab36175e78ee33a36ac6ea0479e3fe2..1d18648b9ef7bee2940a4604bd90a4f7d445a0ff 100644
--- a/core/res/res/layout/chooser_grid_preview_text.xml
+++ b/core/res/res/layout/chooser_grid_preview_text.xml
@@ -44,6 +44,8 @@
android:ellipsize="end"
android:fontFamily="@android:string/config_headlineFontFamily"
android:textColor="?android:attr/textColorPrimary"
+ android:textAlignment="gravity"
+ android:textDirection="locale"
android:maxLines="2"
android:focusable="true"/>
@@ -90,6 +92,8 @@
android:layout_gravity="center_vertical"
android:ellipsize="end"
android:maxLines="2"
+ android:textAlignment="gravity"
+ android:textDirection="locale"
android:textAppearance="@style/TextAppearance.DeviceDefault.WindowTitle"
android:fontFamily="@android:string/config_headlineFontFamily"/>
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index ec54091e5a20b5ebd02deecbe5f75e5e699e921f..3615b9e2f9cb1a213fba9c01dc5d0ffd01667125 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -22,10 +22,11 @@
android:layout_gravity="bottom">
@@ -63,13 +65,17 @@
android:layout_height="@dimen/conversation_icon_size_badged"
android:layout_marginLeft="@dimen/conversation_badge_side_margin"
android:layout_marginTop="@dimen/conversation_badge_side_margin"
+ android:clipChildren="false"
+ android:clipToPadding="false"
>
@@ -119,6 +128,9 @@
android:layout_weight="1">
+
+
-
"Druk is gedeaktiveer deur %s.""Skakel jou werkprofiel aan""Jou persoonlike programme word geblokkeer totdat jy jou werkprofiel aanskakel"
-
-
-
-
+ "Persoonlike programme sal op %1$s om %2$s geblokkeer word. Jou IT-admin laat nie toe dat jou werkprofiel langer as %3$d dae af bly nie."
+ "Skakel aan""Ek""Tablet-opsies""Android TV-opsies"
@@ -1621,7 +1619,6 @@
"Jy het jou ontsluitpatroon %1$d keer verkeerdelik geteken. Na nog %2$d onsuksesvolle pogings, sal jy gevra word om jou foon te ontsluit deur middel van \'n e-posrekening.\n\n Probeer weer oor %3$d sekondes."" — ""Verwyder"
- "Die voorgronddiens wat in die agtergrond begin het vanaf %1$s sal nie ingebruik-toestemming hê in toekomstige R-bouweergawes nie. Raadpleeg asseblief go/r-bg-fgs-restriction en dien \'n foutverslag in.""Verhoog volume bo aanbevole vlak?\n\nOm lang tydperke teen hoë volume te luister, kan jou gehoor beskadig.""Gebruik toeganklikheidkortpad?""Wanneer die kortpad aan is, sal \'n toeganklikheidkenmerk begin word as albei volumeknoppies 3 sekondes lank gedruk word."
@@ -1653,8 +1650,8 @@
"Gebruik kortpad""Kleuromkering""Kleurkorreksie"
- "Het volumesleutels gehou. %1$s aangeskakel."
- "Het volumesleutels gehou. %1$s het afgeskakel"
+ "Het volumesleutels ingehou. %1$s aangeskakel."
+ "Het volumesleutels ingehou. %1$s is afgeskakel""Druk en hou albei volumesleutels drie sekondes lank om %1$s te gebruik""Kies \'n kenmerk om te gebruik wanneer jy op die toeganklikheidknoppie tik:""Kies \'n kenmerk om te gebruik saam met die toeganklikheidgebaar (swiep met twee vingers op van die onderkant van die skerm af):"
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 8291ad70c8bd38032a61c82c7901e09957e6b848..5eb271e754a67b455d11d2c36a65a6745c81c748 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -202,10 +202,8 @@
"ማተም በ%s ተሰናክሏል።""የስራ መገለጫዎን ያብሩት""የስራ መገለጫዎን እስኪያበሩት ድረስ የግል መተግበሪያዎችዎ ታግደዋል"
-
-
-
-
+ "የግል መተግበሪያዎች %1$s%2$s ላይ ይታገዳሉ። የእርስዎ የአይቲ አስተዳዳሪ የሥራ መገለጫዎ ከ%3$d ቀኖች በላይ ጠፍቶ እንዲቆይ አይፈቅዱም።"
+ "አብራ""እኔ""የጡባዊ አማራጮች""Android TV አማራጮች"
@@ -1621,7 +1619,6 @@
"የመክፈቻ ስርዓተ ጥለቱን %1$d ጊዜ በትክክል አልሳሉትም። ከ%2$d ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው ስልክዎን እንዲከፍቱ ይጠየቃሉ።\n\nእባክዎ ከ%3$d ሰከንዶች በኋላ እንደገና ይሞክሩ።"" — ""አስወግድ"
- "ዳራው ከ%1$s የጀመረው የፊት አገልግሎት ወደፊት በሚኖሩት R ግንቦች ላይ ጥቅም ላይ እየዋለ ፈቃድ አይኖረውም። እባክዎ go/r-bg-fgs-restriction እና ፋይል ሳንካ ሪፖርትን ይመልከቱ።""ድምጹ ከሚመከረው መጠን በላይ ከፍ ይበል?\n\nበከፍተኛ ድምጽ ለረጅም ጊዜ ማዳመጥ ጆሮዎን ሊጎዳው ይችላል።""የተደራሽነት አቋራጭ ጥቅም ላይ ይዋል?""አቋራጩ ሲበራ ሁለቱንም የድምጽ አዝራሮች ለ3 ሰከንዶች ተጭኖ መቆየት የተደራሽነት ባህሪን ያስጀምረዋል።"
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index cae96722a9075a3387b5539cc1d8549c8d583f52..1966a223c68002e55a8941a3c6d8eeb11579afa1 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -210,10 +210,8 @@
"تم إيقاف الطباعة بواسطة %s.""تفعيل الملف الشخصي للعمل""تم حظر تطبيقاتك الشخصية إلى أن تفعِّل ملفك الشخصي للعمل."
-
-
-
-
+ "سيتم حظر التطبيقات الشخصية في %1$s في %2$s. لا يسمح مشرف تكنولوجيا المعلومات في مؤسستك بإيقاف الملف الشخصي للعمل أكثر من %3$d يوم."
+ "تفعيل""أنا""خيارات الجهاز اللوحي""خيارات Android TV"
@@ -975,7 +973,7 @@
"إضافة بريد صوتي""للسماح للتطبيق بإضافة رسائل إلى صندوق البريد الصوتي.""تعديل أذونات الموقع الجغرافي للمتصفح"
- "للسماح لأحد التطبيقات بتعديل أذونات الموقع الجغرافي للمتصفح. يمكن أن تستخدم التطبيقات الضارة هذا للسماح بإرسال معلومات الموقع إلى مواقع ويب عشوائية."
+ "للسماح لأحد التطبيقات بتعديل أذونات الموقع الجغرافي للمتصفح. يمكن أن تستخدم التطبيقات الضارة هذا للسماح بإرسال معلومات الموقع إلى مواقع إلكترونية عشوائية.""هل تريد من المتصفح تذكر كلمة المرور هذه؟""ليس الآن""تذكّر"
@@ -1171,7 +1169,7 @@
"منتصف الليل""%1$02d:%2$02d""%1$d:%2$02d:%3$02d"
- "اختيار الكل"
+ "تحديد الكل""قص""نسخ""تعذّر النسخ في الحافظة"
@@ -1393,7 +1391,7 @@
"اختيار إيقاف تصحيح أخطاء USB.""تم تفعيل ميزة \"تصحيح الأخطاء اللاسلكي\".""انقر لإيقاف ميزة \"تصحيح الأخطاء اللاسلكي\"."
- "اختيار إيقاف ميزة \"تصحيح الأخطاء اللاسلكي\""
+ "انقر لإيقاف ميزة \"تصحيح الأخطاء اللاسلكي\"""تم تفعيل وضع \"مفعّل الاختبار\"""يمكنك إجراء إعادة ضبط على الإعدادات الأصلية لإيقاف وضع \"مفعِّل اختبار\".""وحدة التحكّم التسلسلية مفعّلة"
@@ -1709,12 +1707,11 @@
"لقد رسمت نقش فتح القفل بشكل غير صحيح %1$d مرة. بعد إجراء %2$d من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الهاتف باستخدام حساب بريد إلكتروني لإلغاء تأمين الهاتف.\n\n أعد المحاولة خلال %3$d ثانية."" — ""إزالة"
- "لن يتم منح إذن الوصول إلى الموقع الجغرافي أثناء الاستخدام للخدمات التي تعمل في المقدّمة من %1$s والتي تبدأ من الخلفية في إصدارات R القادمة. يُرجى مراجعة go/r-bg-fgs-restriction وتقديم تقرير خطأ.""هل تريد رفع مستوى الصوت فوق المستوى الموصى به؟\n\nقد يضر سماع صوت عالٍ لفترات طويلة بسمعك.""هل تريد استخدام اختصار \"سهولة الاستخدام\"؟""عند تفعيل الاختصار، يؤدي الضغط على زرّي التحكّم في مستوى الصوت معًا لمدة 3 ثوانٍ إلى تفعيل إحدى ميزات إمكانية الوصول.""هل تريد تفعيل ميزات إمكانية الوصول؟"
- "يؤدي الضغط مع الاستمرار على كلا مفتاحَي التحكّم في مستوى الصوت لبضع ثوانٍ إلى تفعيل ميزات إمكانية الوصول. قد يؤدي هذا إلى تغيير كيفية عمل جهازك.\n\nالميزات الحالية:\n%1$s\nيمكنك تغيير الميزات المحددة في الإعدادات > أدوات تمكين الوصول."
+ "يؤدي الضغط مع الاستمرار على كلا مفتاحَي التحكّم في مستوى الصوت لبضع ثوانٍ إلى تفعيل ميزات إمكانية الوصول. قد يؤدي هذا الإجراء إلى تغيير طريقة عمل جهازك.\n\nالميزات الحالية:\n%1$s\nيمكنك تغيير الميزات المحددة في الإعدادات > إمكانية الوصول."" • %1$s\n""هل تريد تفعيل %1$s؟""يؤدي الضغط مع الاستمرار لبضع ثوانٍ على كلا مفتاحَي التحكّم في مستوى الصوت إلى تفعيل %1$s وهي إحدى ميزات إمكانية الوصول. يمكن أن يؤدي هذا الإجراء إلى تغيير كيفية عمل جهازك.\n\nيمكنك تغيير هذا الاختصار لاستخدامه مع ميزة أخرى في الإعدادات > أدوات تمكين الوصول."
@@ -1984,9 +1981,9 @@
"توسيع""تصغير""تبديل التوسيع"
- "منفذ الأجهزة الطرفية المزودة بكابل USB ونظام التشغيل Android"
+ "منفذ الأجهزة الملحقة المزودة بكابل USB ونظام التشغيل Android""Android"
- "منفذ الأجهزة الطرفية المزودة بكابل USB"
+ "منفذ الأجهزة الملحقة المزودة بكابل USB""خيارات أخرى""إغلاق التجاوز""تكبير"
@@ -2190,7 +2187,7 @@
"محادثة جماعية""%1$d+""شخصي"
- "عمل"
+ "للعمل""عرض المحتوى الشخصي""عرض محتوى العمل""تتعذّر مشاركة هذا المحتوى باستخدام تطبيقات العمل"
@@ -2201,9 +2198,9 @@
"لا يسمح لك مشرف تكنولوجيا المعلومات بمشاركة هذا المحتوى باستخدام التطبيقات في ملفك الشخصي.""يتعذّر فتح هذا المحتوى باستخدام التطبيقات الشخصية""لا يسمح لك مشرف تكنولوجيا المعلومات بفتح هذا المحتوى باستخدام التطبيقات في ملفك الشخصي."
- "تم إيقاف الملف الشخصي للعمل مؤقتًا."
+ "الملف الشخصي للعمل متوقف مؤقتًا.""تفعيل"
- "لا يمكن لتطبيقات العمل أن تدعم هذا المحتوى."
+ "لا يمكن لتطبيقات العمل أن تتوافق مع هذا المحتوى.""لا يمكن لتطبيقات العمل أن تفتح هذا المحتوى.""لا يمكن للتطبيقات الشخصية أن تدعم هذا المحتوى.""لا يمكن للتطبيقات الشخصية أن تفتح هذا المحتوى."
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 43feb19fcfdced130a9015843c8f867ec6577397..f2c7c4551de879a00919ee2fda593a069552173d 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -202,10 +202,8 @@
"প্ৰিণ্ট কৰা কাৰ্য %sএ অক্ষম কৰি ৰাখিছে।""কৰ্মস্থানৰ প্ৰ’ফাইলটো অন কৰক""আপুনি নিজৰ কৰ্মস্থানৰ প্ৰ’ফাইলটো অন নকৰালৈকে আপোনাৰ ব্যক্তিগত এপ্সমূহ অৱৰোধ কৰা থাকে"
-
-
-
-
+ "%1$s তাৰিখে %2$s বজাত ব্যক্তিগত এপ্সমূহ অৱৰোধ কৰা হ’ব। আপোনাৰ আইটি প্ৰশাসকে আপোনাৰ কৰ্মস্থানৰ প্ৰ’ফাইলটো %3$d দিনতকৈ বেছি সময়ৰ বাবে অফ কৰি ৰাখিবলৈ অনুমতি নিদিয়ে।"
+ "অন কৰক""মই""টে\'বলেটৰ বিকল্পসমূহ""Android TVৰ বিকল্পসমূহ"
@@ -1621,12 +1619,11 @@
"আপুনি আপোনাৰ ল\'ক খোলাৰ আৰ্হিটো %1$dবাৰ ভুলকৈ আঁকিছে। %2$dতকৈ বেছি বাৰ ভুল আৰ্হি আঁকিলে আপোনাৰ ফ\'নটো কোনো একাউণ্টৰ জৰিয়তে আনলক কৰিবলৈ কোৱা হ\'ব।\n\n %3$d ছেকেণ্ডৰ পিছত আকৌ চেষ্টা কৰক।"" — ""আঁতৰাওক"
- "নেপথ্যই %1$sৰ পৰা আৰম্ভ কৰা অগ্ৰভূমিৰ সেৱাটোৰ ভৱিষ্যতৰ R বিল্ডসমূহত ব্যৱহাৰ হৈ থকা সম্পৰ্কীয় অনুমতি নাথাকিব। অনুগ্ৰহ কৰি go/r-bg-fgs-restriction চাওক আৰু এটা বাগৰিপ\'ৰ্ট ফাইল কৰক।""অনুমোদিত স্তৰতকৈ ওপৰলৈ ভলিউম বঢ়াব নেকি?\n\nদীৰ্ঘ সময়ৰ বাবে উচ্চ ভলিউমত শুনাৰ ফলত শ্ৰৱণ ক্ষমতাৰ ক্ষতি হ\'ব পাৰে।""দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাট ব্যৱহাৰ কৰেনে?""শ্বৰ্টকাটটো অন হৈ থকাৰ সময়ত দুয়োটা ভলিউম বুটাম ৩ ছেকেণ্ডৰ বাবে হেঁচি ধৰি ৰাখিলে এটা সাধ্য সুবিধা আৰম্ভ হ’ব।""সাধ্য-সুবিধাসমূহ অন কৰিবনে?"
- "দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে সাধ্য-সুবিধাসমূহ অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nবর্তমানৰ সুবিধাসমূহ:\n%1$s\nআপুনি ছেটিংসমূহ > সাধ্য-সুবিধাত বাছনি কৰা সুবিধাসমূহ সলনি কৰিব পাৰে।"
+ "দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে সাধ্য-সুবিধাসমূহ অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nবর্তমানৰ সুবিধাসমূহ:\n%1$s\nআপুনি ছেটিংসমূহ > সাধ্য-সুবিধাত কিছুমান নিৰ্দিষ্ট সুবিধা সলনি কৰিব পাৰে।"" • %1$s\n""%1$s অন কৰিবনে?""দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে এটা সাধ্য- সুবিধা %1$s অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nআপুনি ছেটিংসমূহ > সাধ্য-সুবিধাসমূহত এই শ্বৰ্টকাটটো অন্য এটা সুবিধালৈ সলনি কৰিব পাৰে।"
@@ -1654,7 +1651,7 @@
"ৰং বিপৰীতকৰণ""ৰং শুধৰণী""ভলিউম কীসমূহ ধৰি ৰাখক। %1$s অন কৰা হ\'ল।"
- "ভলিউম কীসমূহ ধৰি ৰাখক। %1$s অফ কৰা হ\'ল।"
+ "ভলিউম কী ধৰি ৰাখিছিল। %1$s অফ কৰা হ\'ল।""%1$s ব্যৱহাৰ কৰিবলৈ দুয়োটা ভলিউম বুটাম তিনি ছেকেণ্ডৰ বাবে হেঁচি ৰাখক""আপুনি সাধ্য-সুবিধাৰ বুটামটো টিপিলে ব্যৱহাৰ কৰিবলৈ এটা সুবিধা বাছনি কৰক:""সাধ্য-সুবিধাৰ নির্দেশৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ এটা সুবিধা বাছনি কৰক (দুটা আঙুলিৰে স্ক্রীনখনৰ একেবাৰে তলিৰ পৰা ওপৰলৈ ছোৱাইপ কৰক):"
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index d8d0c2d29d1276d45aadfd85db1ec6bd9aa91aa0..3fb5131ba763ec4f065d19bc5e83360613219bee 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -202,10 +202,8 @@
"Çap %s tərəfindən deaktiv edildi.""İş profilinizi aktiv edin""İş profilinizi aktiv edənədək şəxsi tətbiqləriniz bloklanır"
-
-
-
-
+ "Şəxsi tətbiqlər bu tarixdə bloklanacaq: %1$s, %2$s. İT admini iş profilinizin %3$d gündən çox deaktiv qalmasına icazə vermir."
+ "Aktiv edin""Mən""Planşet seçimləri""Android TV seçimləri"
@@ -1621,7 +1619,6 @@
"Siz artıq modeli %1$d dəfə yanlış daxil etmisiniz.%2$d dəfə də yanlış daxil etsəniz, telefonun kilidinin açılması üçün elektron poçt ünvanınız tələb olunacaq.\n\n %3$d saniyə ərzində yenidən cəhd edin."" - ""Yığışdır"
- "Arxa fonda başladılan %1$s üzrə ön plan xidmətinin gələcək R versiyalarında \"istifadə zamanı\" icazəsi olmayacaq. go/r-bg-fgs-restriction bölməsinə keçin və baq hesabatı göndərin.""Səsin həcmi tövsiyə olunan səviyyədən artıq olsun?\n\nYüksək səsi uzun zaman dinləmək eşitmə qabiliyyətinizə zərər vura bilər.""Əlçatımlılıq Qısayolu istifadə edilsin?""Qısayol aktiv olduqda, hər iki səs düyməsinə 3 saniyə basıb saxlamaqla əlçatımlılıq funksiyası başladılacaq."
@@ -1654,7 +1651,7 @@
"Rəng İnversiyası""Rəng korreksiyası""Səs səviyyəsi düymələrinə basıb saxlayın. %1$s aktiv edildi."
- "Səs səviyyəsi düymələrinə basıb saxlayın. %1$s deaktiv edildi."
+ "Səs səviyyəsi düymələrinə basılaraq saxlanıb. %1$s deaktiv edilib.""%1$s istifadə etmək üçün hər iki səs düyməsini üç saniyə basıb saxlayın""Əlçatımlılıq düyməsinə toxunduqda istifadə edəcəyiniz funksiyanı seçin:""Əlçatımlılıq jesti (iki barmağınızla ekranın aşağısından yuxarı doğru sürüşdürün) ilə istifadə edəcəyiniz funksiyanı seçin:"
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 94439cbb875ed7f4b87063246bde2b7d7c40ff08..7fb53f6e269bbf0d824fd8d86e4a215bd8363c05 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -204,10 +204,8 @@
"Štampanje je onemogućila aplikacija %s.""Uključite poslovni profil""Lične aplikacije su blokirane dok ne uključite poslovni profil"
-
-
-
-
+ "Lične aplikacije će biti blokirane: %1$s u %2$s. IT administrator ne dozvoljava da poslovni profil bude isključen duže od %3$d dana."
+ "Uključi""Ja""Opcije za tablet""Opcije Android TV-a"
@@ -1151,7 +1149,7 @@
"Otvorite pomoću aplikacije %1$s""Otvori""Otvarajte %1$s linkove pomoću"
- "Otvaratej linkove pomoću"
+ "Otvaraj linkove pomoću""Otvarajte linkove pomoću aplikacije %1$s""Otvarajte %1$s linkove pomoću aplikacije %2$s""Dozvoli pristup"
@@ -1643,7 +1641,6 @@
"Nacrtali ste šablon za otključavanje netačno %1$d puta. Posle još %2$d neuspešna(ih) pokušaja, od vas će biti zatraženo da otključate telefon pomoću naloga e-pošte.\n\nProbajte ponovo za %3$d sekunde/i."" – ""Ukloni"
- "Usluga u prvom planu sa %1$s koja je pokrenuta u pozadini neće imati dozvolu tokom korišćenja u budućim R verzijama. Posetite go/r-bg-fgs-restriction i pošaljite izveštaj o grešci.""Želite da pojačate zvuk iznad preporučenog nivoa?\n\nSlušanje glasne muzike duže vreme može da vam ošteti sluh.""Želite li da koristite prečicu za pristupačnost?""Kada je prečica uključena, pritisnite oba dugmeta za jačinu zvuka da biste pokrenuli funkciju pristupačnosti."
@@ -1667,7 +1664,7 @@
"Odbij""Dodirnite neku funkciju da biste počeli da je koristite:""Odaberite funkcije koje ćete koristiti sa dugmetom Pristupačnost"
- "Odaberite funkcije koje ćete koristiti sa tasterom jačine zvuka kao prečicom"
+ "Odaberite funkcije za prečicu tasterom jačine zvuka""Usluga %s je isključena""Izmenite prečice""Gotovo"
@@ -1675,8 +1672,8 @@
"Koristi prečicu""Inverzija boja""Korekcija boja"
- "Zadržali ste tastere za jačinu zvuka. Usluga %1$s je uključena."
- "Zadržali ste tastere za jačinu zvuka. Usluga %1$s je isključena."
+ "Držali ste tastere za jačinu zvuka. Usluga %1$s je uključena."
+ "Držali ste tastere za jačinu zvuka. Usluga %1$s je isključena.""Pritisnite i zadržite oba tastera za jačinu zvuka tri sekunde da biste koristili %1$s""Izaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:""Odaberite funkciju koja će se koristiti pomoću pokreta za pristupačnost (pomoću dva prsta prevucite nagore od dna ekrana):"
@@ -2101,7 +2098,7 @@
"IT administrator vam ne dozvoljava da otvorite ovaj sadržaj pomoću aplikacija na ličnom profilu""Poslovni profil je pauziran""Uključi"
- "Nijedna aplikacija za posao ne može da podržava ovaj sadržaj"
+ "Nijedna aplikacija za posao ne podržava ovaj sadržaj""Nijedna aplikacija za posao ne može da otvori ovaj sadržaj""Nijedna lična aplikacija ne može da podržava ovaj sadržaj""Nijedna lična aplikacija ne može da otvori ovaj sadržaj"
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 158602318221d01321055359e583273fc888ae94..57e6409c93c0794faa590c545415e9aa31f5022e 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -206,10 +206,8 @@
"Друк адключаны ўладальнікам праграмы %s.""Уключыце працоўны профіль""Вашы асабістыя праграмы будуць заблакіраваны, пакуль вы не ўключыце працоўны профіль"
-
-
-
-
+ "Асабістыя праграмы будуць заблакіраваны ў %2$s%1$s. Ваш ІТ-адміністратар не дазволіў выключаць працоўны профіль больш чым на %3$d сут."
+ "Уключыць""Я""Параметры планшэта""Параметры Android TV"
@@ -1665,7 +1663,6 @@
"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: %1$d. Пасля яшчэ некалькiх няўдалых спроб (%2$d) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google.\n\n Паўтарыце спробу праз %3$d с."" — ""Выдалiць"
- "Запушчаны ў фонавым рэжыме асноўны сэрвіс з пакета \"%1$s\" не будзе мець дазволу while-in-use у будучых зборках на мове R. Наведайце сайт go/r-bg-fgs-restriction і адпраўце справаздачу пра памылку.""Павялiчыць гук вышэй рэкамендаванага ўзроўню?\n\nДоўгае праслухоўванне музыкi на вялiкай гучнасцi можа пашкодзiць ваш слых.""Выкарыстоўваць камбінацыю хуткага доступу для спецыяльных магчымасцей?""Калі хуткі доступ уключаны, вы можаце націснуць абедзве кнопкі гучнасці і ўтрымліваць іх 3 секунды, каб запусціць функцыю спецыяльных магчымасцей."
@@ -1689,7 +1686,7 @@
"Адмовіць""Каб пачаць выкарыстоўваць функцыю, націсніце на яе:""Выберыце функцыі, якія будзеце выкарыстоўваць з кнопкай спецыяльных магчымасцей"
- "Выберыце функцыі, якія будзеце выкарыстоўваць са спалучэннем клавішы гучнасці"
+ "Выберыце функцыі для выкарыстання з клавішай гучнасці""Сэрвіс \"%s\" выключаны""Змяніць ярлыкі""Гатова"
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index a337d01b805f0a156ca588b05e9081d53b0c7591..99d7d8651d1ef9470236e6338218bdd4ba899b5d 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -202,10 +202,8 @@
"Отпечатването е деактивиранo от %s.""Служ. потр. профил: Включване""Личните ви приложения са блокирани, докато не включите служебния си потребителски профил"
-
-
-
-
+ "Личните приложения ще бъдат блокирани на %1$s в %2$s. Системният ви администратор не разрешава служебният ви потребителски профил да бъде изключен за повече от %3$d дни."
+ "Включване""Аз""Опции за таблета""Опции за Android TV"
@@ -310,7 +308,7 @@
"Физическа активност""достъп до физическата ви активност""Камера"
- "да прави снимки и записва видеоклипове"
+ "да прави снимки и записва видео""Списъци с обажданията""четене и запис на списъка с телефонните обаждания""Телефон"
@@ -438,9 +436,9 @@
"разпознаване на физическата активност""Това приложение може да разпознава физическата ви активност.""правене на снимки и видеоклипове"
- "Това приложение може по всяко време да прави снимки и да записва видеоклипове посредством камерата."
+ "Това приложение може по всяко време да прави снимки и да записва видео посредством камерата.""Разрешаване на достъп на приложение или услуга до системните камери с цел правене на снимки и видеоклипове"
- "Това привилегировано или системно приложение може по всяко време да прави снимки и да записва видеоклипове посредством системна камера. Необходимо е също на приложението да бъде дадено разрешението android.permission.CAMERA"
+ "Това привилегировано или системно приложение може по всяко време да прави снимки и да записва видео посредством системна камера. Необходимо е също на приложението да бъде дадено разрешението android.permission.CAMERA""Разрешаване на приложение или услуга да получават обратни повиквания за отварянето или затварянето на снимачни устройства.""Това приложение може да получава обратни повиквания, когато снимачно устройство бъде отворено (от кое приложение) или затворено.""контролиране на вибрирането"
@@ -1621,11 +1619,10 @@
"Начертахте неправилно фигурата си за отключване %1$d пъти. След още %2$d неуспешни опита ще бъдете помолени да отключите телефона посредством имейл адрес.\n\n Опитайте отново след %3$d секунди."" – ""Премахване"
- "Задният план, който е стартирал услуга на преден план от %1$s, няма да има разрешение при използване в бъдещите компилации R. Моля, вижте go/r-bg-fgs-restriction и подайте сигнал за програмна грешка.""Да се увеличи ли силата на звука над препоръчителното ниво?\n\nПродължителното слушане при висока сила на звука може да увреди слуха ви.""Искате ли да използвате пряк път към функцията за достъпност?""Когато прекият път е включен, можете да стартирате дадена функция за достъпност, като натиснете двата бутона за силата на звука и ги задържите за 3 секунди."
- "Да се включат ли функциите за достъпност?"
+ "Включване на функциите за достъпност?""Натиснете двата бутона за силата на звука и ги задръжте за няколко секунди, за да включите функциите за достъпност. Това може да промени начина, по който работи устройството ви.\n\nТекущи функции:\n%1$s\nМожете да промените избраните функции от „Настройки“ > „Достъпност“."" • %1$s\n""Да се включи ли %1$s?"
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 18992c9e5d1e244ad23bf83fe86ca3eb6eb91819..d560ce93444021b18213aa6fce63de27ae266f25 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -202,10 +202,8 @@
"%s প্রিন্টিং বন্ধ রেখেছে।""অফিসের প্রোফাইল চালু করুন""আপনার অফিসের প্রোফাইল চালু না করা পর্যন্ত আপনার ব্যক্তিগত অ্যাপ ব্লক থাকে"
-
-
-
-
+ "ব্যক্তিগত অ্যাপ %1$s-এ %2$sটার সময় ব্লক করা হবে। আপনার আইটি অ্যাডমিন আপনার অফিস প্রোফাইল %3$d দিনের বেশি পজ করে রাখার অনুমতি দেয় না।"
+ "চালু করুন""আমাকে""ট্যাবলেট বিকল্পগুলি""Android TV-র বিকল্প"
@@ -240,7 +238,7 @@
"স্ক্রীণ লক""পাওয়ার বন্ধ করুন""ফোন বন্ধ করুন"
- "ফোন আবার চালু করুন"
+ "ফোন রিস্টার্ট করুন""জরুরী""ত্রুটির প্রতিবেদন""সেশন শেষ করুন"
@@ -1621,12 +1619,11 @@
"আপনি আপনার আনলকের প্যাটার্ন আঁকার ক্ষেত্রে %1$d বার ভুল করেছেন৷ আর %2$d বার অসফল প্রচেষ্টা করা হলে আপনাকে একটি ইমেল অ্যাকাউন্ট মারফত আপনার ফোন আনলক করতে বলা হবে৷\n\n %3$d সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"" — ""সরান"
- "%1$s থেকে শুরু হওয়া ফোরগ্রাউন্ড পরিষেবাটির ভবিষ্যতে আর বিল্ডগুলিতে ব্যবহারের অনুমতি নেই। go/r-bg-fgs-restriction দেখুন এবং বাগরিপোর্ট জমা দিন।""প্রস্তাবিত স্তরের চেয়ে বেশি উঁচুতে ভলিউম বাড়াবেন?\n\nউঁচু ভলিউমে বেশি সময় ধরে কিছু শুনলে আপনার শ্রবনশক্তির ক্ষতি হতে পারে।""অ্যাক্সেসযোগ্যতা শর্টকাট ব্যবহার করবেন?""শর্টকাট চালু করা থাকাকালীন দুটি ভলিউম বোতাম একসাথে ৩ সেকেন্ড টিপে ধরে রাখলে একটি অ্যাকসেসিবিলিটি ফিচার চালু হবে।""অ্যাক্সেসিবিলিটি ফিচার চালু করতে চান?"
- "উভয় ভলিউম কী কয়েক সেকেন্ড ধরে থাকলে অ্যাক্সেসিবিলিটি ফিচার চালু হয়ে যাবে। এর ফলে, আপনার ডিভাইস কীভাবে কাজ করবে সেটিতে পরিবর্তন হতে পারে।\n\nবর্তমান ফিচার:\n%1$s\nসেটিংস > অ্যাক্সেসিবিলিটি থেকে আপনি বাছাই করা ফিচার পরিবর্তন করতে পারবেন।"
+ "উভয় ভলিউম কী কয়েক সেকেন্ড ধরে থাকলে অ্যাক্সেসিবিলিটি ফিচার চালু হয়ে যাবে। এর ফলে, আপনার ডিভাইস কীভাবে কাজ করবে সেটিতে পরিবর্তন হতে পারে।\n\nবর্তমান ফিচার:\n%1$s\nসেটিংস > অ্যাক্সেসিবিলিটি বিকল্প থেকে আপনি বাছাই করা ফিচার পরিবর্তন করতে পারবেন।"" • %1$s\n""%1$s চালু করতে চান?""উভয় ভলিউম কী কয়েক সেকেন্ড ধরে থাকলে %1$s চালু হয়ে যাবে। এটি একটি অ্যাক্সেসিবিলিটি ফিচার। এর ফলে, আপনার ডিভাইস কীভাবে কাজ করবে সেটিতে পরিবর্তন হতে পারে।\n\nসেটিংস > অ্যাক্সেসিবিলিটি থেকে আপনি এই শর্টকাট পরিবর্তন করতে পারবেন।"
@@ -2065,9 +2062,9 @@
"আইটি অ্যাডমিন আপনার ব্যক্তিগত প্রোফাইল ব্যবহার করে অ্যাপের মাধ্যমে এই কন্টেন্ট শেয়ার করার অনুমতি দেয় না""ব্যক্তিগত অ্যাপের মাধ্যমে খোলা যাবে না""আইটি অ্যাডমিন আপনার ব্যক্তিগত প্রোফাইল ব্যবহার করে অ্যাপের মাধ্যমে এই কন্টেন্ট খোলার অনুমতি দেয় না"
- "কাজের প্রোফাইল পজ করা আছে"
+ "অফিস প্রোফাইল বন্ধ করা আছে""চালু করুন"
- "এই ধরনের কন্টেন্ট অফিসের অ্যাপের মাধ্যমে খুলে দেখা যাবে না"
+ "এই ধরনের কন্টেন্ট অফিস অ্যাপের মাধ্যমে খোলা যাবে না""এই ধরনের কন্টেন্ট অফিসের অ্যাপের মাধ্যমে খুলে দেখা যাবে না""এই ধরনের কন্টেন্ট ব্যক্তিগত অ্যাপের মাধ্যমে খোলা বা দেখা যাবে না""এই ধরনের কন্টেন্ট ব্যক্তিগত অ্যাপের মাধ্যমে খোলা যাবে না"
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 7e8fe5342d361c71689196ef3f3bd799afcde68e..a4c1ba42377c1701699ddff0840036ccd2714bae 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -204,10 +204,8 @@
"Štampanje je onemogućila aplikacija %s.""Uključite radni profil""Vaše lične aplikacije će biti blokirane dok ne uključite radni profil"
-
-
-
-
+ "Lične aplikacije će biti blokirane %1$s u %2$s. Vaš IT administrator ne dozvoljava da radni profil bude isključen duže od %3$d dana."
+ "Uključi""Ja""Opcije tableta""Opcije Android TV uređaja"
@@ -216,7 +214,7 @@
"Uključi bežičnu vezu""Isključi bežičnu vezu""Zaključavanje ekrana"
- "Isključi telefon"
+ "Isključi""Zvuk zvona isključen""Zvuk zvona na vibraciji""Zvuk zvona uključen"
@@ -240,7 +238,7 @@
"Opcije Android TV uređaja""Opcije telefona""Zaključavanje ekrana"
- "Isključi telefon"
+ "Isključi""Napajanje""Ponovo pokreni""Hitno"
@@ -1150,10 +1148,10 @@
"Otvori koristeći""Otvori koristeći %1$s""Otvori"
- "Otvaranje %1$s linkova pomoću"
- "Otvaranje linkova pomoću"
- "Otvaranje linkova pomoću aplikacije %1$s"
- "Otvaranje %1$s linkova pomoću aplikacije %2$s"
+ "Otvaranje %1$s linkova pomoću preglednika/aplikacije"
+ "Otvaranje linkova pomoću preglednika"
+ "Otvaranje linkova pomoću preglednika %1$s"
+ "Otvaranje %1$s linkova pomoću preglednika %2$s""Dozvoli pristup""Uredi koristeći""Uredi koristeći %1$s"
@@ -1330,7 +1328,7 @@
"Priključeni uređaj nije kompatibilan s ovim telefonom. Dodirnite da saznate više.""Otklanjanje grešaka putem USB-a je uspostavljeno""Dodirnite da isključite otklanjanje grešaka putem USB-a"
- "Odaberite da onemogućite ispravljanje grešaka koristeći USB"
+ "Odaberite da onemogućite otklanjanje grešaka putem USB-a""Bežično otklanjanje grešaka je povezano""Dodirnite da isključite bežično otklanjanje grešaka""Odaberite da onemogućite bežično otklanjanje grešaka."
@@ -1643,17 +1641,16 @@
"Pogrešno ste nacrtali uzorak za otključavanje %1$d puta. Ako napravite još %2$d pokušaja bez uspjeha, od vas će se tražiti da otključate telefon pomoću e-pošte. \n\n Pokušajte ponovo za %3$d s."" — ""Ukloni"
- "Usluge iz prvog plana započete u pozadini s web lokacije %1$s neće imati odobrenje za funkciju \"za vrijeme korištenja\" u budućim R verzijama. Pogledajte go/r-bg-fgs-restriction i podnesite izvještaj o greškama.""Želite li pojačati zvuk iznad preporučenog nivoa?\n\nDužim slušanjem glasnog zvuka možete oštetiti sluh.""Želite li koristiti Prečicu za pristupačnost?""Kada je prečica uključena, pritiskom i držanjem oba dugmeta za jačinu zvuka u trajanju od 3 sekunde pokrenut će se funkcija pristupačnosti.""Uključiti funkcije pristupačnosti?"
- "Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkcije pristupačnosti. Ovo može uticati na način rada uređaja.\n\nTrenutne funkcije:\n%1$s\nOdabrane funkcije možete izmijeniti u odjeljku Postavke > Pristupačnost."
+ "Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkcije pristupačnosti. Ovo može uticati na način rada uređaja.\n\nTrenutne funkcije:\n%1$s\nOdabrane funkcije možete promijeniti u odjeljku Postavke > Pristupačnost."" • %1$s\n""Uključiti %1$s?""Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkciju pristupačnosti %1$s. Ovo može promijeniti način rada uređaja.\n\nOvu prečicu možete zamijeniti drugom funkcijom u odjeljku Postavke > Pristupačnost.""Uključi"
- "Nemoj uključivati"
+ "Nemoj uključiti""UKLJUČENO""ISKLJUČENO""Dozvoliti da usluga %1$s ima punu kontrolu nad vašim uređajem?"
@@ -1667,7 +1664,7 @@
"Odbij""Dodirnite funkciju da je počnete koristiti:""Odaberite funkcije koje ćete koristiti s dugmetom Pristupačnost"
- "Odaberite funkcije koje ćete koristiti s tipkom prečice za jačinu zvuka"
+ "Odaberite funkcije koje ćete koristiti pomoću prečice tipke za jačinu zvuka""Usluga %s je isključena""Uredi prečice""Gotovo"
@@ -1955,7 +1952,7 @@
"Mape i navigacija""Produktivnost""Memorija uređaja"
- "Otklanjanje grešaka putem uređaja spojenog na USB"
+ "Otklanjanje grešaka putem USB-a""sat""minuta""Postavljanje vremena"
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index d048a7f8d4a5723e4fb4752e47b75880a80bc5a4..0a5907ef4274958eea9898c7ac9541fa9a747445 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -202,10 +202,8 @@
"%s ha desactivat la impressió.""Activa el perfil de treball""Les teves aplicacions personals estan bloquejades fins que activis el perfil de treball"
-
-
-
-
+ "Les aplicacions personals es bloquejaran el dia %1$s a les %2$s. L\'administrador de TI no permet desactivar el teu perfil de treball durant més de: %3$d dies."
+ "Activa""Mi""Opcions de la tauleta""Opcions d\'Android TV"
@@ -239,7 +237,7 @@
"Opcions del telèfon""Bloqueig de pantalla""Apaga"
- "Engegada"
+ "Engega""Reinicia""Emergència""Informe d\'error"
@@ -266,7 +264,7 @@
"Configuració""Assistència""Assist. per veu"
- "Bloq. de seguretat"
+ "Bloqueig de seguretat""+999""Notificació nova""Teclat virtual"
@@ -1621,7 +1619,6 @@
"Has dibuixat el patró de desbloqueig %1$d vegades de manera incorrecta. Si falles %2$d vegades més, se\'t demanarà que desbloquegis el telèfon amb un compte de correu electrònic.\n\n Torna-ho a provar d\'aquí a %3$d segons."" — ""Elimina"
- "El servei en primer pla (%1$s) iniciat en segon pla no tindrà permís durant l\'ús en compilacions R posteriors. Consulta la pàgina go/r-bg-fgs-restriction i presenta un informe d\'errors.""Vols apujar el volum per sobre del nivell recomanat?\n\nSi escoltes música a un volum alt durant períodes llargs, pots danyar-te l\'oïda.""Vols fer servir la drecera d\'accessibilitat?""Si la drecera està activada, prem els dos botons de volum durant 3 segons per iniciar una funció d\'accessibilitat."
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index a0c5ff0ae8c6761363a5993bacf34b82256366c0..3ce57233cc099828c823e891003bae3f17bde753 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -206,10 +206,8 @@
"Aplikace %s tisk zakazuje.""Zapněte pracovní profil""Vaše osobní aplikace jsou zablokovány, dokud nezapnete pracovní profil"
-
-
-
-
+ "Osobní aplikace budou zablokovány %1$s v %2$s. Administrátor IT nepovoluje vypnutí pracovního profilu na déle než tento počet dní: %3$d."
+ "Zapnout""Já""Možnosti tabletu""Možnosti zařízení Android TV"
@@ -1665,7 +1663,6 @@
"Již %1$dkrát jste nesprávně nakreslili své heslo odemknutí. Po %2$d dalších neúspěšných pokusech budete požádáni o odemčení telefonu pomocí e-mailového účtu.\n\n Zkuste to znovu za %3$d s."" – ""Odebrat"
- "Služba v popředí z balíčku %1$s, která byla spuštěna na pozadí, v budoucích sestavenách typu R nebude mít oprávnění ke spuštění při používání. Přejděte na adresu go/r-bg-fgs-restriction a vyplňte zprávu o chybě.""Zvýšit hlasitost nad doporučenou úroveň?\n\nDlouhodobý poslech hlasitého zvuku může poškodit sluch.""Použít zkratku přístupnosti?""Když je tato zkratka zapnutá, můžete funkci přístupnosti spustit tím, že na tři sekundy podržíte obě tlačítka hlasitosti."
@@ -1698,7 +1695,7 @@
"Převrácení barev""Oprava barev""Byla podržena tlačítka hlasitosti. Služba %1$s je zapnutá."
- "Byla podržena tlačítka hlasitosti. Služba %1$s je vypnutá."
+ "Byla podržena tlačítka hlasitosti. Služba %1$s byla vypnuta.""Chcete-li používat službu %1$s, tři sekundy podržte stisknutá obě tlačítka hlasitosti""Určete, jakou funkci aktivujete klepnutím na tlačítko přístupnosti:""Určete, jakou funkci aktivujete pomocí gesta přístupnosti (přejetí dvěma prsty ze spodní části obrazovky nahoru):"
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index d240f30d6b28340157735b7305674a1a063ca2f9..bd5a0d37dd85e7f74ce32549804e93901c8f880f 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -202,10 +202,8 @@
"Udskrivning er deaktiveret af %s.""Aktivér din arbejdsprofil""Dine personlige apps er blokeret, indtil du aktiverer din arbejdsprofil"
-
-
-
-
+ "Personlige apps bliver blokeret %1$s kl. %2$s. Din it-administrator tillader ikke, at din arbejdsprofil deaktiveres i mere end %3$d dage."
+ "Aktivér""Mig""Valgmuligheder for tabletcomputeren""Valgmuligheder for Android TV"
@@ -266,7 +264,7 @@
"Indstillinger""Assistance""Taleassistent"
- "Lukning"
+ "Lås enhed""999+""Ny notifikation""Virtuelt tastatur"
@@ -1621,7 +1619,6 @@
"Du har tegnet dit oplåsningsmønster forkert %1$d gange. Efter %2$d yderligere mislykkede forsøg til vil du blive bedt om at låse din telefon op ved hjælp af en mailkonto.\n\n Prøv igen om %3$d sekunder."" – ""Fjern"
- "Tjenesten i forgrunden fra %1$s, der starter i baggrunden, vil i fremtidige R-builds ikke have tilladelse, mens den er i brug. Se go/r-bg-fgs-restriction, og indsend en fejlrapport.""Vil du skrue højere op end det anbefalede lydstyrkeniveau?\n\nDu kan skade hørelsen ved at lytte til meget høj musik over længere tid.""Vil du bruge genvejen til Hjælpefunktioner?""Når genvejen er aktiveret, kan du starte en hjælpefunktion ved at trykke på begge lydstyrkeknapper i tre sekunder."
@@ -1645,7 +1642,7 @@
"Afvis""Tryk på en funktion for at bruge den:""Vælg, hvilke funktioner du vil bruge med knappen Hjælpefunktioner"
- "Vælg, hvilke funktioner du vil bruge med genvejen via lydstyrkeknapperne"
+ "Vælg de funktioner, du vil bruge via lydstyrkeknapperne""%s er blevet deaktiveret""Rediger genveje""Udfør"
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 0e425afd207ae91b05efcfad881818590b5a7910..a2a4adc782c25392a590b17fd07b3a67900aefc0 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -202,10 +202,8 @@
"Drucken wurde von %s deaktiviert.""Arbeitsprofil aktivieren""Deine privaten Apps werden blockiert, bis du dein Arbeitsprofil aktivierst"
-
-
-
-
+ "Private Apps werden am %1$s um %2$s blockiert. Dein IT-Administrator hat festgelegt, dass dein Arbeitsprofil nicht länger als %3$d Tage pausiert werden darf."
+ "Aktivieren""Eigene""Tablet-Optionen""Android TV-Optionen"
@@ -1621,7 +1619,6 @@
"Du hast dein Entsperrungsmuster %1$d-mal falsch gezeichnet. Nach %2$d weiteren erfolglosen Versuchen wirst du aufgefordert, dein Telefon mithilfe eines E-Mail-Kontos zu entsperren.\n\n Versuche es in %3$d Sekunden erneut."" – ""Entfernen"
- "Der Dienst im Vordergrund von %1$s, der im Hintergrund gestartet wurde, hat in zukünftigen R-Builds keine Zugriffsberechtigung mehr während der Nutzung. Siehe dazu go/r-bg-fgs-restriction und reiche einen Fehlerbericht ein.""Lautstärke über den Schwellenwert anheben?\n\nWenn du über einen längeren Zeitraum Musik in hoher Lautstärke hörst, kann dies dein Gehör schädigen.""Verknüpfung für Bedienungshilfen verwenden?""Wenn die Verknüpfung aktiviert ist, kannst du die beiden Lautstärketasten drei Sekunden lang gedrückt halten, um eine Bedienungshilfe zu starten."
@@ -1645,7 +1642,7 @@
"Ablehnen""Zum Auswählen der gewünschten Funktion tippen:""Funktionen auswählen, die du mit der Schaltfläche \"Bedienungshilfen\" verwenden möchtest"
- "Funktionen auswählen, die du mit der Verknüpfung für die Lautstärketaste verwenden möchtest"
+ "Funktionen für Verknüpfung mit Lautstärketaste auswählen""%s wurde deaktiviert""Verknüpfungen bearbeiten""Fertig"
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 65872f43f46e783aeb4dc18dee11480b34d1d670..916ad5f1ac21ced8be13c771b42d53305ca43c3d 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -202,10 +202,8 @@
"Η εκτύπωση απενεργοποιήθηκε από τον χρήστη %s.""Ενεργοπ. το προφίλ εργασίας""Οι προσωπικές σας εφαρμογές αποκλείονται μέχρι να ενεργοποιήσετε το προφίλ εργασίας σας."
-
-
-
-
+ "Οι προσωπικές εφαρμογές θα αποκλειστούν στις %1$s και ώρα %2$s. Ο διαχειριστής IT δεν επιτρέπει το προφίλ εργασίας να παραμένει απενεργοποιημένο για περισσότερες από %3$d ημέρες."
+ "Ενεργοποίηση""Για εμένα""Επιλογές tablet""Επιλογές Android TV"
@@ -1309,7 +1307,7 @@
"Εντοπίστηκε αναλογικό αξεσουάρ ήχου""Η συνδεδεμένη συσκευή δεν είναι συμβατή με αυτό το τηλέφωνο. Πατήστε για να μάθετε περισσότερα.""Συνδέθηκε ο εντοπισμός σφαλμάτων USB"
- "Απενεργοποιήστε τον εντοπισμό/διόρθ. σφαλμάτων USB"
+ "Πατήστε για απενεργοποίηση εντοπισμού/διόρθ. σφαλμάτων USB""Επιλογή για απενεργοποίηση του εντοπισμού σφαλμάτων USB.""Συνδέθηκε ο ασύρματος εντοπισμός σφαλμάτων""Πατήστε, για να απενεργοποιήσετε τον ασύρματο εντοπισμό σφαλμάτων"
@@ -1621,7 +1619,6 @@
"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα %1$d φορές. Μετά από %2$d ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου.\n\n Δοκιμάστε ξανά σε %3$d δευτερόλεπτα."" — ""Κατάργηση"
- "Η υπηρεσία στο προσκήνιο που ξεκίνησε από το παρασκήνιο από το πακέτο %1$s δεν θα έχει άδεια πρόσβασης μόνο κατά τη χρήση σε μελλοντικές εκδόσεις R. Ανατρέξτε στο go/r-bg-fgs-restriction και υποβάλετε αναφορά σφάλματος.""Αυξάνετε την ένταση ήχου πάνω από το επίπεδο ασφαλείας;\n\nΑν ακούτε μουσική σε υψηλή ένταση για μεγάλο χρονικό διάστημα ενδέχεται να προκληθεί βλάβη στην ακοή σας.""Να χρησιμοποιείται η συντόμευση προσβασιμότητας;""Όταν η συντόμευση είναι ενεργοποιημένη, το πάτημα και των δύο κουμπιών έντασης ήχου για 3 δευτερόλεπτα θα ξεκινήσει μια λειτουργία προσβασιμότητας."
@@ -1654,7 +1651,7 @@
"Αντιστροφή χρωμάτων""Διόρθωση χρωμάτων""Τα πλήκτρα έντασης είναι πατημένα. %1$s ενεργοποιήθηκε."
- "Τα πλήκτρα έντασης είναι πατημένα. %1$s απενεργοποιήθηκε."
+ "Τα πλήκτρα έντασης είναι πατημένα. %1$s: απενεργοποιημένο""Πατήστε παρατεταμένα και τα δύο κουμπιά έντασης ήχου για τρία δευτερόλεπτα, ώστε να χρησιμοποιήσετε την υπηρεσία %1$s""Επιλέξτε μια λειτουργία που θα χρησιμοποιείται κατά το πάτημα του κουμπιού προσβασιμότητας:""Επιλέξτε μια λειτουργία που θα χρησιμοποιείται με την κίνηση προσβασιμότητας (σύρετε με δύο δάχτυλα προς τα επάνω από το κάτω μέρος της οθόνης):"
@@ -2057,7 +2054,7 @@
"Εργασία""Προσωπική προβολή""Προβολή εργασίας"
- "Δεν είναι δυνατή η κοινοποίηση αυτού του περιεχομένου με εφαρμογές εργασιών"
+ "Δεν είναι δυνατή η κοινοποίηση αυτού του περιεχομένου με εφαρμογές εργασίας""Ο διαχειριστής IT δεν σας επιτρέπει να κοινοποιήσετε αυτό το περιεχόμενο με εφαρμογές στο προφίλ εργασίας σας.""Δεν είναι δυνατό το άνοιγμα με εφαρμογές εργασίας""Ο διαχειριστής IT δεν σας επιτρέπει να ανοίξετε αυτό το περιεχόμενο με εφαρμογές στο προφίλ εργασίας σας."
@@ -2067,8 +2064,8 @@
"Ο διαχειριστής IT δεν σας επιτρέπει να ανοίξετε αυτό το περιεχόμενο με εφαρμογές στο προσωπικό προφίλ σας.""Το προφίλ εργασίας σας έχει τεθεί σε παύση.""Ενεργοποίηση"
- "Καμία εφαρμογή εργασιών δεν μπορεί να υποστηρίξει αυτό το περιεχόμενο."
- "Καμία εφαρμογή εργασιών δεν μπορεί να ανοίξει αυτό το περιεχόμενο."
+ "Καμία εφαρμογή εργασίας δεν μπορεί να υποστηρίξει αυτό το περιεχόμενο."
+ "Καμία εφαρμογή εργασίας δεν μπορεί να ανοίξει αυτό το περιεχόμενο.""Καμία προσωπική εφαρμογή δεν μπορεί να υποστηρίξει αυτό το περιεχόμενο.""Καμία προσωπική εφαρμογή δεν μπορεί να ανοίξει αυτό το περιεχόμενο.""PIN ξεκλειδώματος δικτύου κάρτας SIM"
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 3416e2d53ba6b3d5dfce956ab737cedc92a58541..551325d1c691b087d485e9309c6f2488c669cd3f 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -202,10 +202,8 @@
"Printing disabled by %s.""Turn on your work profile""Your personal apps are blocked until you turn on your work profile"
-
-
-
-
+ "Personal apps will be blocked on %1$s at %2$s. Your IT admin doesn’t allow your work profile to stay off for more than %3$d days."
+ "Turn on""Me""Tablet options""Android TV options"
@@ -318,7 +316,7 @@
"Body sensors""access sensor data about your vital signs""Retrieve window content"
- "Inspect the content of a window that you\'re interacting with."
+ "Inspect the content of a window you\'re interacting with.""Turn on Explore by Touch""Tapped items will be spoken aloud and the screen can be explored using gestures.""Observe text that you type"
@@ -1621,7 +1619,6 @@
"You have incorrectly drawn your unlock pattern %1$d times. After %2$d more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in %3$d seconds."" — ""Remove"
- "The background started foreground service from %1$s will not have while-in-use permission in future R builds. Please see go/r-bg-fgs-restriction and file a bug report.""Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing.""Use Accessibility Shortcut?""When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."
@@ -1652,7 +1649,7 @@
"Turn off Shortcut""Use Shortcut""Colour Inversion"
- "Colour Correction"
+ "Colour correction""Held volume keys. %1$s turned on.""Held volume keys. %1$s turned off.""Press and hold both volume keys for three seconds to use %1$s"
@@ -1768,7 +1765,7 @@
"Confirm new PIN""Create a PIN for modifying restrictions""PINs don\'t match. Try again."
- "PIN is too short. Must be at least 4 digits."
+ "PIN is too short. Must be at least four digits."Try again in %d secondsTry again in 1 second
@@ -1795,9 +1792,9 @@
"Updated by your admin""Deleted by your admin""OK"
- "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like “Hey Google”\n\n""Learn more"
- "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like “Hey Google”"
- "To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."
+ "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'\n\n""Learn more"
+ "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'"
+ "To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you\'re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.""Turn on Data Saver?""Turn on"
@@ -1915,7 +1912,7 @@
"Conference Call""Tooltip""Games"
- "Music & Audio"
+ "Music and audio""Movies & Video""Photos & Images""Social & Communication"
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 4d04a3e0d826dccf80ed72adafe7cce90aa684ce..4d3ca94b9c91a59e6860b0452bf777247b5636b4 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -202,10 +202,8 @@
"Printing disabled by %s.""Turn on your work profile""Your personal apps are blocked until you turn on your work profile"
-
-
-
-
+ "Personal apps will be blocked on %1$s at %2$s. Your IT admin doesn’t allow your work profile to stay off for more than %3$d days."
+ "Turn on""Me""Tablet options""Android TV options"
@@ -318,10 +316,10 @@
"Body sensors""access sensor data about your vital signs""Retrieve window content"
- "Inspect the content of a window that you\'re interacting with."
+ "Inspect the content of a window you\'re interacting with.""Turn on Explore by Touch""Tapped items will be spoken aloud and the screen can be explored using gestures."
- "Observe text that you type"
+ "Observe text you type""Includes personal data such as credit card numbers and passwords.""Control display magnification""Control the display\'s zoom level and positioning."
@@ -684,7 +682,7 @@
"Change the screen lock.""Lock the screen""Control how and when the screen locks."
- "Delete all data"
+ "Erase all data""Erase the tablet\'s data without warning by performing a factory data reset.""Delete your Android TV device\'s data without warning by performing a factory data reset.""Erase the phone\'s data without warning by performing a factory data reset."
@@ -1621,7 +1619,6 @@
"You have incorrectly drawn your unlock pattern %1$d times. After %2$d more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in %3$d seconds."" — ""Remove"
- "The background started foreground service from %1$s will not have while-in-use permission in future R builds. Please see go/r-bg-fgs-restriction and file a bug report.""Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing.""Use Accessibility Shortcut?""When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."
@@ -1651,8 +1648,8 @@
"Done""Turn off Shortcut""Use Shortcut"
- "Colour Inversion"
- "Colour Correction"
+ "Colour inversion"
+ "Colour correction""Held volume keys. %1$s turned on.""Held volume keys. %1$s turned off.""Press and hold both volume keys for three seconds to use %1$s"
@@ -1768,7 +1765,7 @@
"Confirm new PIN""Create a PIN for modifying restrictions""PINs don\'t match. Try again."
- "PIN is too short. Must be at least 4 digits."
+ "PIN is too short. Must be at least four digits."Try again in %d secondsTry again in 1 second
@@ -1795,9 +1792,9 @@
"Updated by your admin""Deleted by your admin""OK"
- "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like “Hey Google”\n\n""Learn more"
- "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like “Hey Google”"
- "To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."
+ "To extend battery life, Battery Saver:\n\n• Turns on Dark theme\n• Turns off or restricts background activity, some visual effects and other features like \"Hey Google\"\n\n""Learn more"
+ "To extend battery life, Battery Saver:\n\n• Turns on Dark theme\n• Turns off or restricts background activity, some visual effects and other features like \"Hey Google\""
+ "To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you\'re currently using can access data, but may do so less frequently. This may mean, for example, that images don\'t display until you tap them.""Turn on Data Saver?""Turn on"
@@ -1915,7 +1912,7 @@
"Conference Call""Tooltip""Games"
- "Music & Audio"
+ "Music and audio""Movies & Video""Photos & Images""Social & Communication"
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 3416e2d53ba6b3d5dfce956ab737cedc92a58541..779fdff5935154c1e155d8d47ae84b1a7da72544 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -202,10 +202,8 @@
"Printing disabled by %s.""Turn on your work profile""Your personal apps are blocked until you turn on your work profile"
-
-
-
-
+ "Personal apps will be blocked on %1$s at %2$s. Your IT admin doesn’t allow your work profile to stay off for more than %3$d days."
+ "Turn on""Me""Tablet options""Android TV options"
@@ -1621,7 +1619,6 @@
"You have incorrectly drawn your unlock pattern %1$d times. After %2$d more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in %3$d seconds."" — ""Remove"
- "The background started foreground service from %1$s will not have while-in-use permission in future R builds. Please see go/r-bg-fgs-restriction and file a bug report.""Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing.""Use Accessibility Shortcut?""When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."
@@ -1652,7 +1649,7 @@
"Turn off Shortcut""Use Shortcut""Colour Inversion"
- "Colour Correction"
+ "Colour correction""Held volume keys. %1$s turned on.""Held volume keys. %1$s turned off.""Press and hold both volume keys for three seconds to use %1$s"
@@ -1768,7 +1765,7 @@
"Confirm new PIN""Create a PIN for modifying restrictions""PINs don\'t match. Try again."
- "PIN is too short. Must be at least 4 digits."
+ "PIN is too short. Must be at least four digits."Try again in %d secondsTry again in 1 second
@@ -1795,8 +1792,8 @@
"Updated by your admin""Deleted by your admin""OK"
- "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like “Hey Google”\n\n""Learn more"
- "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like “Hey Google”"
+ "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'\n\n""Learn more"
+ "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'""To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.""Turn on Data Saver?""Turn on"
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 3416e2d53ba6b3d5dfce956ab737cedc92a58541..c290d9e8bf6ecf1fbea0c74da5a8471186c14532 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -202,10 +202,8 @@
"Printing disabled by %s.""Turn on your work profile""Your personal apps are blocked until you turn on your work profile"
-
-
-
-
+ "Personal apps will be blocked on %1$s at %2$s. Your IT admin doesn’t allow your work profile to stay off for more than %3$d days."
+ "Turn on""Me""Tablet options""Android TV options"
@@ -318,7 +316,7 @@
"Body sensors""access sensor data about your vital signs""Retrieve window content"
- "Inspect the content of a window that you\'re interacting with."
+ "Inspect the content of a window you\'re interacting with.""Turn on Explore by Touch""Tapped items will be spoken aloud and the screen can be explored using gestures.""Observe text that you type"
@@ -1008,7 +1006,7 @@
"hour""hours""min"
- "mins"
+ "Min.""sec""secs""week"
@@ -1621,7 +1619,6 @@
"You have incorrectly drawn your unlock pattern %1$d times. After %2$d more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in %3$d seconds."" — ""Remove"
- "The background started foreground service from %1$s will not have while-in-use permission in future R builds. Please see go/r-bg-fgs-restriction and file a bug report.""Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing.""Use Accessibility Shortcut?""When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."
@@ -1652,7 +1649,7 @@
"Turn off Shortcut""Use Shortcut""Colour Inversion"
- "Colour Correction"
+ "Color correction""Held volume keys. %1$s turned on.""Held volume keys. %1$s turned off.""Press and hold both volume keys for three seconds to use %1$s"
@@ -1768,7 +1765,7 @@
"Confirm new PIN""Create a PIN for modifying restrictions""PINs don\'t match. Try again."
- "PIN is too short. Must be at least 4 digits."
+ "PIN is too short. Must be at least four digits."Try again in %d secondsTry again in 1 second
@@ -1795,8 +1792,8 @@
"Updated by your admin""Deleted by your admin""OK"
- "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like “Hey Google”\n\n""Learn more"
- "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like “Hey Google”"
+ "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'\n\n""Learn more"
+ "To extend battery life, Battery Saver:\n\n•Turns on Dark theme\n•Turns off or restricts background activity, some visual effects and other features like \'Hey Google\'""To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.""Turn on Data Saver?""Turn on"
@@ -1915,7 +1912,7 @@
"Conference Call""Tooltip""Games"
- "Music & Audio"
+ "Music and audio""Movies & Video""Photos & Images""Social & Communication"
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 36380b82286409977a32cf3286403aa127246b19..fef02fbc3daa821d669bb6235ff174585d1803ca 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -202,10 +202,8 @@
"Printing disabled by %s.""Turn on your work profile""Your personal apps are blocked until you turn on your work profile"
-
-
-
-
+ "Personal apps will be blocked on %1$s at %2$s. Your IT admin doesn’t allow your work profile to stay off for more than %3$d days."
+ "Turn on""Me""Tablet options""Android TV options"
@@ -1621,7 +1619,6 @@
"You have incorrectly drawn your unlock pattern %1$d times. After %2$d more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in %3$d seconds."" — ""Remove"
- "The background started foreground service from %1$s will not have while-in-use permission in future R builds. Please see go/r-bg-fgs-restriction and file a bugreport.""Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing.""Use Accessibility Shortcut?""When the shortcut is on, pressing both volume buttons for 3 seconds will start an accessibility feature."
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 7f511aec6febe24352476fed09dacc219c1c6caf..6439172d025c3eb5e91fc5481b1dac809d5297ec 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -202,10 +202,8 @@
"%s inhabilitó la impresión.""Activa tu perfil de trabajo""Las apps personales estarán bloqueadas hasta que actives tu perfil de trabajo"
-
-
-
-
+ "Se bloquearán las apps personales el %1$s a la(s) %2$s. Tu administrador de TI no permite que mantengas tu perfil de trabajo desactivado durante más de %3$d días."
+ "Activar""Yo""Opciones de tablet""Opciones de Android TV"
@@ -1621,12 +1619,11 @@
"Dibujaste incorrectamente tu patrón de desbloqueo %1$d veces. Luego de %2$d intentos incorrectos más, se te solicitará que desbloquees tu dispositivo mediante el uso de una cuenta de correo.\n\n Vuelve a intentarlo en %3$d segundos."" — ""Eliminar"
- "El servicio que pasó del segundo al primer plano de %1$s no tendrá permiso durante el uso en las próximas compilaciones de R. Ve a go/r-bg-fgs-restriction y envía un informe de errores.""¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar a un alto volumen durante largos períodos puede dañar tu audición.""¿Usar acceso directo de accesibilidad?""Cuando la combinación de teclas está activada, puedes presionar los botones de volumen durante 3 segundos para iniciar una función de accesibilidad.""¿Quieres activar las funciones de accesibilidad?"
- "Si mantienes presionadas ambas teclas de volumen durante unos segundos, se activarán las funciones de accesibilidad. Esto podría cambiar la manera en la que funciona tu dispositivo.\n\nFunciones actuales:\n%1$s\nPuedes cambiar las funciones seleccionadas en Configuración > Accesibilidad."
+ "Si mantienes presionadas las dos teclas de volumen durante unos segundos, se activarán las funciones de accesibilidad. Esto puede cambiar el funcionamiento de tu dispositivo.\n\nFunciones actuales:\n%1$s\nPuedes cambiar las funciones seleccionadas en Configuración > Accesibilidad."" • %1$s\n""¿Quieres activar %1$s?""Si mantienes presionadas ambas teclas de volumen durante unos segundos, se activará la función de accesibilidad %1$s. Esto podría cambiar la forma en que funciona tu dispositivo.\n\nPuedes cambiar este acceso directo a otra función en Configuración > Accesibilidad."
@@ -1645,7 +1642,7 @@
"Rechazar""Presiona una función para comenzar a usarla:""Selecciona las funciones a utilizar con el botón de accesibilidad"
- "Selecciona las funciones a utilizar con la combinación de teclas de volumen"
+ "Selecciona las funciones a usar con las teclas de volumen""Se desactivó %s""Editar accesos directos""Listo"
@@ -1653,8 +1650,8 @@
"Usar acceso directo""Inversión de color""Corrección de color"
- "Al mantener presionadas las teclas de volumen, se activó %1$s."
- "Al mantener presionadas las teclas de volumen, se desactivó %1$s."
+ "Como mantuviste presionadas las teclas de volumen, se activó %1$s."
+ "Se presionaron las teclas de volumen. Se desactivó %1$s.""Mantén presionadas ambas teclas de volumen durante tres segundos para usar %1$s""Elige una función para usar cuando pulses el botón accesibilidad:""Elige la función que se usará cuando realices el gesto de accesibilidad (deslizar dos dedos hacia arriba desde la parte inferior de la pantalla):"
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 657649316bfe00c8e4f612faa24f2133c29b3ebe..5e245de3376ea414d2a2ebff6ddb62d111dd8f15 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -202,10 +202,8 @@
"%s ha inhabilitado la impresión.""Activa tu perfil de trabajo""Tus aplicaciones personales estarán bloqueadas hasta que actives tu perfil de trabajo"
-
-
-
-
+ "Las aplicaciones personales se bloquearán el %1$s a las %2$s. Tu administrador de TI no permite que tu perfil de trabajo esté más de %3$d días desactivado."
+ "Activar""Yo""Opciones del tablet""Opciones de Android TV"
@@ -1151,7 +1149,7 @@
"Capturar imagen con %1$s""Capturar imagen""Usar siempre para esta acción"
- "Utiliza otra aplicación"
+ "Usar otra aplicación""Para borrar los valores predeterminados, accede a Ajustes del sistema > Aplicaciones > Descargadas.""Selecciona una acción""Elegir una aplicación para el dispositivo USB"
@@ -1313,7 +1311,7 @@
"Seleccionar para inhabilitar la depuración USB""Depuración inalámbrica conectada""Toca para desactivar la depuración inalámbrica"
- "Selecciona para inhabilitar la depuración inalámbrica."
+ "Toca para desactivar la depuración inalámbrica.""Modo de agente de prueba habilitado""Restablece los ajustes de fábrica para inhabilitar el modo de agente de prueba.""Se ha habilitado la consola en serie"
@@ -1621,12 +1619,11 @@
"Has fallado %1$d veces al dibujar el patrón de desbloqueo. Si fallas otras %2$d veces, deberás usar una cuenta de correo electrónico para desbloquear el teléfono.\n\n Inténtalo de nuevo en %3$d segundos."" — ""Quitar"
- "El servicio en primer plano que se inició en segundo plano en %1$s no tendrá permisos en compilaciones R futuras mientras se estén usando. Ve a go/r-bg-fgs-restriction y envía un informe de errores.""¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar sonidos fuertes durante mucho tiempo puede dañar los oídos.""¿Utilizar acceso directo de accesibilidad?""Si el acceso directo está activado, pulsa los dos botones de volumen durante 3 segundos para iniciar una función de accesibilidad."
- "¿Quieres activar las funciones de accesibilidad?"
- "Al mantener pulsadas ambas teclas de volumen durante unos segundos, se activan las funciones de accesibilidad, que pueden cambiar el funcionamiento del dispositivo.\n\nFunciones actuales:\n%1$s\nPuedes cambiar las funciones seleccionadas en Ajustes > Accesibilidad."
+ "¿Activar funciones de accesibilidad?"
+ "Al mantener pulsadas las dos teclas de volumen durante unos segundos, se activan las funciones de accesibilidad, que pueden cambiar el funcionamiento del dispositivo.\n\nFunciones actuales:\n%1$s\nPuedes cambiar las funciones seleccionadas en Ajustes > Accesibilidad."" • %1$s\n""¿Quieres activar %1$s?""Al mantener pulsadas ambas teclas de volumen durante unos segundos se activa %1$s, una función de accesibilidad. Esta función puede modificar el funcionamiento del dispositivo.\n\nPuedes asignar este acceso directo a otra función en Ajustes > Accesibilidad."
@@ -1644,8 +1641,8 @@
"Permitir""Denegar""Toca una función para empezar a usarla:"
- "Seleccionar qué funciones usar con el botón Accesibilidad"
- "Seleccionar qué funciones usar con la tecla de volumen"
+ "Selecciona qué funciones usar con el botón Accesibilidad"
+ "Selecciona qué funciones usar con la tecla de volumen""Se ha desactivado %s""Editar accesos directos""Listo"
@@ -1653,8 +1650,8 @@
"Utilizar acceso directo""Inversión de color""Corrección de color"
- "Al mantener pulsadas las teclas de volumen, %1$s se ha activado."
- "Al mantener pulsadas las teclas de volumen, %1$s se ha desactivado."
+ "Al mantener pulsadas las teclas de volumen, se ha activado %1$s."
+ "Se han mantenido pulsadas las teclas de volumen. Se ha desactivado %1$s.""Para utilizar %1$s, mantén pulsadas ambas teclas de volumen durante 3 segundos""Selecciona la función que se utilizará cuando toques el botón Accesibilidad:""Elige la función que se utilizará con el gesto de accesibilidad (deslizar dos dedos hacia arriba desde la parte inferior de la pantalla):"
@@ -2057,10 +2054,10 @@
"Trabajo""Ver contenido personal""Ver contenido de trabajo"
- "No se puede compartir con una aplicación de trabajo"
- "Tu administrador de TI no te permite compartir este contenido con ninguna aplicación de tu perfil de trabajo"
+ "No se puede compartir con aplicaciones de trabajo"
+ "Tu administrador de TI no te permite compartir este contenido con aplicaciones de tu perfil de trabajo""No se puede abrir con una aplicación de trabajo"
- "Tu administrador de TI no te permite abrir este contenido con ninguna aplicación de tu perfil de trabajo"
+ "Tu administrador de TI no te permite abrir este contenido con aplicaciones de tu perfil de trabajo""No se puede compartir con una aplicación personal""Tu administrador de TI no te permite compartir este contenido con ninguna aplicación de tu perfil personal""No se puede abrir con una aplicación personal"
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 8b99b44f64db0754447ef1deb7c054fe305c3ab3..6b5741cb6913440d3010065b8bc8666861f5fb06 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -202,10 +202,8 @@
"Rakendus %s on printimise keelanud.""Lülitage oma tööprofiil sisse""Teie isiklikud rakendused on blokeeritud, kuni lülitate oma tööprofiili sisse"
-
-
-
-
+ "Isiklikud rakendused blokeeritakse %1$s kell %2$s. IT-administraator ei luba teie tööprofiili üle %3$d päeva väljalülitatuna hoida."
+ "Lülita sisse""Mina""Tahvelarvuti valikud""Android TV valikud"
@@ -1621,15 +1619,14 @@
"Joonistasite oma avamismustri %1$d korda valesti. Pärast veel %2$d ebaõnnestunud katset palutakse teil telefon avada meilikontoga.\n\n Proovige uuesti %3$d sekundi pärast."" — ""Eemalda"
- "Taustal käitatud esiplaanil oleval teenusel paketist %1$s ei ole tulevastes R-järkudes luba „kui kasutuses”. Vaadake saiti go/r-bg-fgs-restriction ja esitage veaaruanne.""Kas suurendada helitugevuse taset üle soovitatud taseme?\n\nPikaajaline valju helitugevusega kuulamine võib kuulmist kahjustada.""Kas kasutada juurdepääsetavuse otseteed?""Kui otsetee on sisse lülitatud, käivitab mõlema helitugevuse nupu kolm sekundit all hoidmine juurdepääsetavuse funktsiooni.""Kas lülitada juurdepääsufunktsioonid sisse?"
- "Hoidke mõlemat helitugevuse nuppu mõni sekund all, et juurdepääsufunktsioonid sisse lülitada. See võib teie seadme tööviisi muuta.\n\nPraegused funktsioonid:\n%1$s\nValitud funktsioone saab muuta jaotises Seaded > Juurdepääsetavus."
+ "Hoidke juurdepääsufunktsioonide sisselülitamiseks mõlemat helitugevuse klahvi mõni sekund all. See võib teie seadme tööviisi muuta.\n\nPraegused funktsioonid:\n%1$s\nValitud funktsioone saab muuta jaotises Seaded > Juurdepääsetavus."" • %1$s\n""Kas lülitada %1$s sisse?"
- "Kui hoiate mõlemat helitugevuse nuppu mõni sekund all, lülitatakse sisse juurdepääsufunktsioon %1$s. See võib teie seadme tööviisi muuta.\n\nSelle otsetee saab asendada muu otseteega jaotises Seaded > Juurdepääsetavus."
+ "Kui hoiate mõlemat helitugevuse klahvi mõni sekund all, lülitatakse juurdepääsufunktsioon %1$s sisse. See võib teie seadme tööviisi muuta.\n\nSelle otsetee saab asendada muu otseteega jaotises Seaded > Juurdepääsetavus.""Lülita sisse""Ära lülita sisse""SEES"
@@ -1645,7 +1642,7 @@
"Keela""Puudutage funktsiooni, et selle kasutamist alustada.""Valige funktsioonid, mida juurdepääsetavuse nupuga kasutada"
- "Valige funktsioonid, mida helitugevuse nupu otseteega kasutada"
+ "Valige helitugevuse nupu otsetee funktsioonid""%s on välja lülitatud""Muuda otseteid""Valmis"
@@ -1653,13 +1650,13 @@
"Kasuta otseteed""Värvide ümberpööramine""Värvide korrigeerimine"
- "Helitugevuse nuppe hoiti all. %1$s lülitati sisse."
- "Helitugevuse nuppe hoiti all. %1$s lülitati välja."
+ "Helitugevuse klahve hoiti all. %1$s lülitati sisse."
+ "Helitugevuse klahve hoiti all. %1$s lülitati välja.""Teenuse %1$s kasutamiseks hoidke kolm sekundit all mõlemat helitugevuse klahvi""Valige, millist funktsiooni kasutada, kui vajutate juurdepääsetavuse nuppu:""Valige, millist funktsiooni juurdepääsetavuse liigutusega (kahe sõrmega ekraanikuval alt üles pühkimine) kasutada:""Valige, millist funktsiooni juurdepääsetavuse liigutusega (kolme sõrmega ekraanikuval alt üles pühkimine) kasutada:"
- "Funktsioonide vahel vahetamiseks vajutage pikalt juurdepääsetavuse nuppu."
+ "Funktsioonide vahel vahetamiseks vajutage juurdepääsetavuse nuppu pikalt.""Funktsioonide vahel vahetamiseks pühkige kahe sõrmega üles ja hoidke.""Funktsioonide vahel vahetamiseks pühkige kolme sõrmega üles ja hoidke.""Suurendus"
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 5d0b90cea7d5d23f7f3d3c883ee5a84aa1212847..71958e88e99c549a14aed0fd6d758cdf19f363bf 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -202,10 +202,8 @@
"%s aplikazioak desgaitu egin du inprimatzeko aukera.""Aktibatu laneko profila""Aplikazio pertsonalak blokeatuta egongo dira laneko profila aktibatzen duzun arte"
-
-
-
-
+ "Aplikazio pertsonalak egun eta ordu honetan blokeatuko dira: %1$s, %2$s. IKT saileko administratzaileak ez dizu ematen baimenik laneko profila %3$d egunez baino gehiagoz desaktibatuta edukitzeko."
+ "Aktibatu""Ni""Tabletaren aukerak""Android TV gailuaren aukerak"
@@ -424,9 +422,9 @@
"atzitu kokapen-hornitzaileen komando gehigarriak""Kokapen-hornitzailearen agindu gehigarriak atzitzeko baimena ematen die aplikazioei. Horrela, agian aplikazioek GPSaren edo bestelako kokapenaren iturburuen funtzionamenduan eragina izan dezakete.""lortu kokapen zehatza aurreko planoan bakarrik"
- "Erabiltzen ari zarenean, aplikazioak kokapen zehatza lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan. Bateria-erabilera areagotzen du horrek."
+ "Abian denean, aplikazioak kokapen zehatza lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan. Bateria-erabilera areagotzen du horrek.""atzitu gutxi gorabeherako kokapena aurreko planoan bakarrik"
- "Erabiltzen ari zarenean, aplikazioak gutxi gorabeherako kokapena lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan."
+ "Abian denean, aplikazioak gutxi gorabeherako kokapena lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan.""atzitu kokapena atzeko planoan""Aplikazioak kokapena atzi dezake, baita aplikazioa erabiltzen ari ez zarenean ere.""aldatu audio-ezarpenak"
@@ -514,7 +512,7 @@
"Telefonoa WiMAX sareetara konektatzeko edo haietatik deskonektatzeko baimena ematen die aplikazioei.""partekatu Bluetooth bidezko gailuekin""Tabletaren Bluetooth konfigurazioa ikusteko eta parekatutako gailuekin konexioak egiteko eta onartzeko baimena ematen die aplikazioei."
- "Android TV gailuaren Bluetooth konexioaren konfigurazioa ikusteko eta parekatutako gailuekin konexioak sortzeko eta onartzeko baimena ematen die aplikazioei."
+ "Android TV gailuaren Bluetooth bidezko konexioaren konfigurazioa ikusteko eta parekatutako gailuekin konexioak sortzeko eta onartzeko baimena ematen die aplikazioei.""Telefonoaren Bluetooth konfigurazioa ikusteko eta parekatutako gailuekin konexioak egiteko eta onartzeko baimena ematen die aplikazioei.""NFC bidezko ordainketa-zerbitzu lehenetsiari buruzko informazioa""Aplikazioari baimena ematen dio NFC bidezko ordainketa-zerbitzu lehenetsiari buruzko informazioa jasotzeko, hala nola erregistratutako laguntzaileak eta ibilbidearen helmuga."
@@ -1309,11 +1307,11 @@
"Audio-osagarri analogiko bat hauteman da""Erantsitako gailua ez da telefono honekin bateragarria. Sakatu informazio gehiago lortzeko.""USB bidezko arazketa konektatuta"
- "Sakatu USB bidezko arazketa desaktibatzeko"
+ "Sakatu hau USB bidezko arazketa desaktibatzeko""Hautatu USB bidezko arazketa desgaitzeko."
- "Konektatu da hari gabeko arazketa"
+ "Hari gabeko arazketa konektatuta dago""Sakatu hau hari gabeko arazketa desaktibatzeko"
- "Hautatu hau hari gabeko arazketa desgaitzeko"
+ "Hautatu hau hari gabeko arazketa desgaitzeko.""Proba-materialeko modua gaitu da""Proba-materialaren modua desgaitzeko, berrezarri jatorrizko datuak.""Serie-kontsola gaituta"
@@ -1621,15 +1619,14 @@
"Desblokeatzeko eredua oker marraztu duzu %1$d aldiz. Beste %2$d aldiz oker marrazten baduzu, telefonoa posta-kontu baten bidez desblokeatzeko eskatuko dizugu.\n\n Saiatu berriro %3$d segundo barru."" — ""Kendu"
- "%1$s webgunearen atzeko planoak hasitako aurreko planoko zerbitzuak ez du izango erabili bitarteko baimenik etorkizuneko R konpilazioetan. Joan go/r-bg-fgs-restriction atalera eta egin akatsaren txostena.""Bolumena gomendatutako mailatik gora igo nahi duzu?\n\nMusika bolumen handian eta denbora luzez entzuteak entzumena kalte diezazuke.""Erabilerraztasun-lasterbidea erabili nahi duzu?""Lasterbidea aktibatuta dagoenean, bi bolumen-botoiak hiru segundoz sakatuta abiaraziko da erabilerraztasun-eginbidea.""Erabilerraztasun-eginbideak aktibatu nahi dituzu?"
- "Eduki sakatuta bolumen-teklak segundo batzuez erabilerraztasun-eginbideak aktibatzeko. Honen bidez, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nUneko eginbideak:\n%1$s\nAukeratutako eginbideak aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."
+ "Eduki sakatuta bolumen-botoiak segundo batzuez erabilerraztasun-eginbideak aktibatzeko. Hori eginez gero, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nUneko eginbideak:\n%1$s\nHautatutako eginbideak aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."" • %1$s\n""%1$s aktibatu nahi duzu?"
- "Eduki sakatuta bolumen-teklak segundo batzuez %1$s izeneko erabilerraztasun-eginbidea aktibatzeko. Honen bidez, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nLasterbide hau beste eginbide batengatik aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."
+ "Eduki sakatuta bolumen-botoiak segundo batzuez %1$s izeneko erabilerraztasun-eginbidea aktibatzeko. Honen bidez, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nLasterbide hau beste eginbide batengatik aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera.""Aktibatu""Ez aktibatu""AKTIBATUTA"
@@ -1645,7 +1642,7 @@
"Ukatu""Eginbide bat erabiltzen hasteko, saka ezazu:""Aukeratu zein eginbide erabili nahi duzun Erabilerraztasuna botoiarekin"
- "Aukeratu zein eginbide erabili nahi duzun bolumen-teklen lasterbidearekin"
+ "Aukeratu zein eginbide erabili nahi duzun bolumen-botoien lasterbidearekin""Desaktibatu da %s""Editatu lasterbideak""Eginda"
@@ -1653,9 +1650,9 @@
"Erabili lasterbidea""Koloreen alderantzikatzea""Koloreen zuzenketa"
- "Eduki sakatuta bolumen-teklak. Aktibatu da %1$s."
- "Eduki sakatuta bolumen-teklak. Desaktibatu da %1$s."
- "%1$s erabiltzeko, eduki sakatuta bolumen-tekla biak hiru segundoz"
+ "Bolumen-botoiak sakatuta eduki direnez, %1$s aktibatu egin da."
+ "Bolumen-botoiak sakatuta eduki direnez, %1$s desaktibatu egin da."
+ "%1$s erabiltzeko, eduki sakatuta bi bolumen-botoiak hiru segundoz""Aukeratu zein eginbide erabili nahi duzun Erabilerraztasuna botoia sakatzean:""Aukeratu zein eginbide erabili nahi duzun erabilerraztasun-keinuarekin (hau da, bi hatz pantailaren behealdetik gora pasatzean):""Aukeratu zein eginbide erabili nahi duzun erabilerraztasun-keinuarekin (hau da, hiru hatz pantailaren behealdetik gora pasatzean):"
@@ -1929,7 +1926,7 @@
"Ezarri ordua""Idatzi balio duen ordu bat""Idatzi ordua"
- "Aldatu testu modura ordua zehazteko."
+ "Ordua idazteko, aldatu testua idazteko metodora.""Aldatu erloju modura ordua zehazteko.""Betetze automatikoaren aukerak""Gorde betetze automatikoarekin erabiltzeko"
@@ -2026,7 +2023,7 @@
"%1$s kalkulu-orria""Aurkezpena""%1$s aurkezpena"
- "Bluetooth konexioak aktibatuta jarraituko du hegaldi moduan"
+ "Bluetooth-ak aktibatuta jarraituko du hegaldi moduan""Kargatzen"%s + %d fitxategi
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 29ac9e7fc1d3118280132b69b70f6f39dda61b29..c1d0ae6ba59dfbddcedb155c93a045a0f1747a8f 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -202,10 +202,8 @@
"%s چاپ کردن را غیرفعال کرده است.""نمایه کاریتان را روشن کنید""تا زمانیکه نمایه کاریتان را روشن نکنید، برنامههای شخصیتان مسدودند"
-
-
-
-
+ "برنامههای شخصی در تاریخ %1$s ساعت %2$s مسدود خواهند شد. سرپرست فناوری اطلاعات اجازه نمیدهد نمایه کاری شما بیشتر از %3$d روز خاموش بماند."
+ "روشن کردن""من""گزینههای رایانهٔ لوحی""گزینههای Android TV"
@@ -516,8 +514,8 @@
"به برنامه اجازه میدهد تا پیکربندی بلوتوث در رایانهٔ لوحی را مشاهده کند و اتصال با دستگاههای مرتبط را برقرار کرده و بپذیرد.""به برنامه اجازه میدهد پیکربندی بلوتوث را در دستگاه Android TV شما ببیند، و اتصالات با دستگاههای مرتبطشده را بپذیرد یا این اتصالات را برقرار کند.""به برنامه اجازه میدهد تا پیکربندی بلوتوث در تلفن را مشاهده کند، و اتصالات دستگاههای مرتبط را برقرار کرده و بپذیرد."
- "اطلاعات ترجیحی سرویس پولی NFC"
- "به برنامه اجازه میدهد اطلاعات ترجیحی «سرویس پولی NFC»، مانند کمکهای ثبتشده و مقصد مسیر را دریافت کند."
+ "اطلاعات ترجیحی سرویس پولی «ارتباط میدان نزدیک» (NFC)"
+ "به برنامه اجازه میدهد اطلاعات ترجیحی سرویس پولی «ارتباط میدان نزدیک» (NFC)، مانند کمکهای ثبتشده و مقصد مسیر را دریافت کند.""کنترل ارتباط راه نزدیک""به برنامه اجازه میدهد تا با تگهای «ارتباط میدان نزدیک» (NFC)، کارتها و فایلخوان ارتباط برقرار کند.""غیرفعال کردن قفل صفحه شما"
@@ -556,7 +554,7 @@
"چهره احراز هویت شد، لطفاً تأیید را فشار دهید""سختافزار اثرانگشت در دسترس نیست.""ذخیره اثر انگشت ممکن نیست. لطفاً یک اثر انگشت موجود را حذف کنید."
- "مهلت زمانی ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."
+ "درنگ ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید.""عملکرد اثر انگشت لغو شد.""کاربر عملیات اثر انگشت را لغو کرد""تلاشهای زیادی انجام شده است. بعداً دوباره امتحان کنید."
@@ -1621,7 +1619,6 @@
"شما الگوی بازگشایی قفل خود را %1$d بار اشتباه کشیدهاید. پس از %2$d تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل تلفن خود را باز کنید.\n\n لطفاً پس از %3$d ثانیه دوباره امتحان کنید."" — ""حذف"
- "سرویس پیشنمای %1$s، که در پسزمینه شروع شده، در ساختهای آتی R اجازهٔ حین استفاده نخواهد داشت. لطفاً به go/r-bg-fgs-restriction بروید و گزارش اشکال ارسال کنید.""میزان صدا را به بالاتر از حد توصیه شده افزایش میدهید؟\n\nگوش دادن به صداهای بلند برای مدت طولانی میتواند به شنواییتان آسیب وارد کند.""از میانبر دسترسپذیری استفاده شود؟""وقتی میانبر روشن باشد، با فشار دادن هردو دکمه صدا بهمدت ۳ ثانیه ویژگی دسترسپذیری فعال میشود."
@@ -1630,7 +1627,7 @@
" • %1$s\n""%1$s روشن شود؟""با پایین نگه داشتن هردو کلید میزان صدا بهمدت چند ثانیه، %1$s (یکی از ویژگیهای دسترسپذیری) روشن میشود. با این کار نحوه عملکرد دستگاهتان تغییر میکند.\n\nمیتوانید در «تنظیمات > دسترسپذیری»،این میانبر را به ویژگی دیگری تغییر دهید."
- "روشن کردن"
+ "روشن شود""روشن نشود""روشن""خاموش"
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index e785f0097531ff2174850a0530dfd453312b4bab..6aab2c3cc0b6023f7f4620e3b2589be8aae056b4 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -202,10 +202,8 @@
"%s on poistanut tulostuksen käytöstä.""Laita työprofiilisi päälle""Henkilökohtaiset sovelluksesi estetään, kunnes laitat työprofiilisi päälle"
-
-
-
-
+ "Henkilökohtaiset sovellukset estetään %1$s klo %2$s. IT-järjestelmänvalvoja ei salli työprofiilisi pysyä pois päältä yli %3$d päivää."
+ "Laita päälle""Minä""Tablet-laitteen asetukset""Android TV ‑vaihtoehdot"
@@ -240,7 +238,7 @@
"Näytön lukitus""Katkaise virta""Virta"
- "Uudelleenkäynnistys"
+ "Käynnistä uudelleen""Hätäpuhelu""Virheraportti""Lopeta käyttökerta"
@@ -1621,7 +1619,6 @@
"Piirsit lukituksenpoistokuvion väärin %1$d kertaa. Jos piirrät kuvion väärin vielä %2$d kertaa, sinua pyydetään poistamaan puhelimesi lukitus sähköpostitilin avulla.\n\n Yritä uudelleen %3$d sekunnin kuluttua."" – ""Poista"
- "Taustalla aloitettu etualan palvelu (%1$s) ei ole käytön aikana sallittu R:n tulevissa versioissa. Lue go/r-bg-fgs-restriction ja lähetä virheraportti.""Nostetaanko äänenvoimakkuus suositellun tason yläpuolelle?\n\nPitkäkestoinen kova äänenvoimakkuus saattaa heikentää kuuloa.""Käytetäänkö esteettömyyden pikanäppäintä?""Kun pikanäppäin on käytössä, voit käynnistää esteettömyystoiminnon pitämällä molempia äänenvoimakkuuspainikkeita painettuna kolmen sekunnin ajan."
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 804b5c9b00c45a4517d53f8d407b629e27328f2e..2b42c12a9fea0cfd8c88cfb62cc05c0ed83064be 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -202,10 +202,8 @@
"Impression désactivée par %s.""Activer profil professionnel""Vos applications personnelles sont bloquées jusqu\'à ce que vous activiez votre profil professionnel"
-
-
-
-
+ "Les applications personnelles seront bloquées le %1$s à %2$s. Votre administrateur informatique ne vous autorise pas à laisser votre profil professionnel désactivé pendant plus de %3$d jours."
+ "Activer""Moi""Options de la tablette""Options d\'Android TV"
@@ -1621,7 +1619,6 @@
"Vous avez dessiné un schéma de déverrouillage incorrect à %1$d reprises. Si vous échouez encore %2$d fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans %3$d secondes."" — ""Supprimer"
- "Le service de premier plan qui a démarré en arrière-plan provenant de %1$s ne disposera pas de l\'autorisation pendant l\'utilisation dans les futures versions R. Veuillez accéder à go/r-bg-fgs-restriction et envoyer un rapport de bogue.""Augmenter le volume au-dessus du niveau recommandé?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives.""Utiliser le raccourci d\'accessibilité?""Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour lancer une fonctionnalité d\'accessibilité."
@@ -1654,7 +1651,7 @@
"Inversion des couleurs""Correction des couleurs""Touches de volume maintenues enfoncées. %1$s activé."
- "Touches de volume maintenues enfoncées. %1$s désactivé."
+ "Touches de volume maintenues enfoncées. Service %1$s désactivé.""Maintenez enfoncées les deux touches de volume pendant trois secondes pour utiliser %1$s""Choisissez une fonctionnalité à utiliser lorsque vous touchez le bouton d\'accessibilité :""Choisissez une fonctionnalité à utiliser lorsque vous utilisez le geste d\'accessibilité (balayer l\'écran de bas en haut avec deux doigts) :"
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 410275761404f20cb172121b712ad73c183cddbd..08a84884888016e817d3344197be8f2120b978ee 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -200,12 +200,10 @@
"Les données de votre appareil vont être effacées""Impossible d\'utiliser l\'application d\'administration. Les données de votre appareil vont maintenant être effacées.\n\nSi vous avez des questions, contactez l\'administrateur de votre organisation.""Impression désactivée par %s."
- "Activez profil professionnel"
- "Vos applications personnelles sont bloquées jusqu\'à ce que vous activiez votre profil professionnel"
-
-
-
-
+ "Activez votre profil pro"
+ "Vos applications personnelles seront bloquées jusqu\'à ce que vous activiez votre profil professionnel"
+ "Vos applications personnelles seront bloquées le %1$s à %2$s. Votre administrateur informatique ne vous autorise pas à désactiver votre profil professionnel pendant plus de %3$d jours."
+ "Activer""Moi""Options de la tablette""Options Android TV"
@@ -1621,7 +1619,6 @@
"Vous avez dessiné un schéma de déverrouillage incorrect à %1$d reprises. Si vous échouez encore %2$d fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans %3$d secondes."" — ""Supprimer"
- "Le service de premier plan qui a démarré en arrière-plan et provenant de %1$s ne disposera pas de l\'autorisation \"pendant l\'utilisation\" dans les futurs builds R. Veuillez accéder à go/r-bg-fgs-restriction et envoyer un rapport de bug.""Augmenter le volume au dessus du niveau recommandé ?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives.""Utiliser le raccourci d\'accessibilité ?""Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour démarrer une fonctionnalité d\'accessibilité."
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 91a8d058570960bf43f256ebbab2a3aebea06f81..1732d084696139a521b2ce8cd47a6c699c2f10f7 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -202,10 +202,8 @@
"%s desactivou a impresión.""Activa o perfil de traballo""As túas aplicacións persoais están bloqueadas ata que actives o teu perfil de traballo"
-
-
-
-
+ "As aplicacións persoais bloquearanse o %1$s á seguinte hora: %2$s. O administrador de TI non permite que o teu perfil de traballo estea desactivado máis de %3$d días."
+ "Activar""Eu""Opcións da tableta""Opcións de Android TV"
@@ -856,7 +854,7 @@
"Só chamadas de emerxencia""Bloqueada pola rede""A tarxeta SIM está bloqueada con código PUK."
- "Consulta a guía do usuario ou ponte en contacto co servizo de asistencia ao cliente."
+ "Consulta a guía para usuarios ou ponte en contacto co servizo de asistencia ao cliente.""A tarxeta SIM está bloqueada.""Desbloqueando tarxeta SIM…""Debuxaches incorrectamente o padrón de desbloqueo %1$d veces. \n\nTéntao de novo en %2$d segundos."
@@ -1621,7 +1619,6 @@
"Debuxaches o padrón de desbloqueo incorrectamente %1$d veces. Se realizas %2$d intentos incorrectos máis, terás que desbloquear o teléfono a través dunha conta de correo electrónico.\n\n Téntao de novo dentro de %3$d segundos."" — ""Eliminar"
- "Nas futuras compilacións R, o servizo en primeiro plano iniciado en segundo plano desde %1$s non terá permiso mentres estea en uso. Consulta go/r-bg-fgs-restriction e presenta un informe de erros.""Queres subir o volume máis do nivel recomendado?\n\nA reprodución de son a un volume elevado durante moito tempo pode provocar danos nos oídos.""Queres utilizar o atallo de accesibilidade?""Cando o atallo está activado, podes premer os dous botóns de volume durante 3 segundos para iniciar unha función de accesibilidade."
@@ -1645,7 +1642,7 @@
"Denegar""Tocar unha función para comezar a utilizala:""Escoller as funcións que queres utilizar co botón Accesibilidade"
- "Escoller as funcións que queres utilizar co atallo da tecla de volume"
+ "Escolle as funcións que queres utilizar co atallo da tecla de volume""%s: desactivouse""Editar atallos""Feito"
@@ -1653,8 +1650,8 @@
"Utilizar atallo""Inversión de cor""Corrección de cor"
- "Teclas de volume premidas. Servizo %1$s activado."
- "Teclas de volume premidas. Servizo %1$s desactivado."
+ "Teclas de volume premidas. Activouse o servizo %1$s."
+ "Teclas de volume premidas. Desactivouse %1$s.""Mantén premidas as teclas do volume durante tres segudos para usar %1$s""Escolle a función que queres utilizar cando toques o botón Accesibilidade:""Escolle a función que queres usar co xesto de accesibilidade (pasa dous dedos cara arriba desde a parte inferior da pantalla):"
@@ -1788,7 +1785,7 @@
"%1$s do traballo""2.º %1$s do traballo""3.º %1$s do traballo"
- "Solicitar PIN para soltar fixación"
+ "Solicitar PIN para deixar de fixar""Solicitar un padrón de desbloqueo antes de deixar de fixar a pantalla""Solicitar un contrasinal para deixar de fixar a pantalla""Instalado polo teu administrador"
@@ -2032,7 +2029,7 @@
%s + %d ficheiros%s + %d ficheiro
- "Non hai persoas recomendadas coas que compartir contido"
+ "Non hai recomendacións de persoas coas que compartir contido""Lista de aplicacións""Esta aplicación non está autorizada a realizar gravacións, pero pode capturar audio a través deste dispositivo USB.""Inicio"
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index ae95949ff3a0c0dfe184a1f9f986f57c13750b76..f445883a801b79cea96a4a02b6d6d5c7f9bc2d0b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -202,10 +202,8 @@
"%s દ્વારા પ્રિન્ટ કરવાનું બંધ કરાયું છે.""કાર્યાલયની પ્રોફાઇલ ચાલુ કરો""જ્યાં સુધી તમે કાર્યાલયની પ્રોફાઇલ ચાલુ ન કરો ત્યાં સુધી તમારી વ્યક્તિગત ઍપ બ્લૉક કરેલી રહે છે"
-
-
-
-
+ "વ્યક્તિગત ઍપને %1$sના રોજ %2$s વાગ્યે બ્લૉક કરવામાં આવશે. તમારા IT વ્યવસ્થાપક તમારી ઑફિસની પ્રોફાઇલને %3$d દિવસ કરતાં વધુ સમય માટે બંધ રાખવાની મંજૂરી આપતા નથી."
+ "ચાલુ કરો""હું""ટેબ્લેટ વિકલ્પો""Android TVના વિકલ્પો"
@@ -1621,7 +1619,6 @@
"તમે તમારી અનલૉક પૅટર્ન %1$d વખત ખોટી રીતે દોરી. હજી %2$d અસફળ પ્રયાસ પછી, તમને ઇમેઇલ એકાઉન્ટનો ઉપયોગ કરીને ફોનને અનલૉક કરવાનું કહેવામાં આવશે.\n\n%3$d સેકન્ડમાં ફરીથી પ્રયાસ કરો."" — ""દૂર કરો"
- "બૅકગ્રાઉન્ડમાં શરૂ થયેલી %1$sની ફોરગ્રાઉન્ડ સેવા પાસે ભવિષ્યની R બિલ્ડમાં ઉપયોગમાં હોય તે સમયની પરવાનગી હશે નહીં. કૃપા કરીને go/r-bg-fgs-restriction જુઓ અને ભૂલનો અહેવાલ દાખલ કરો.""ભલામણ કરેલ સ્તરની ઉપર વૉલ્યૂમ વધાર્યો?\n\nલાંબા સમય સુધી ઊંચા અવાજે સાંભળવું તમારી શ્રવણક્ષમતાને નુકસાન પહોંચાડી શકે છે.""ઍક્સેસિબિલિટી શૉર્ટકટનો ઉપયોગ કરીએ?""જ્યારે શૉર્ટકટ ચાલુ હોય, ત્યારે બન્ને વૉલ્યૂમ બટનને 3 સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા શરૂ થઈ જશે."
@@ -2057,10 +2054,10 @@
"ઑફિસ""વ્યક્તિગત વ્યૂ""ઑફિસ વ્યૂ"
- "આને ઑફિસ માટેની ઍપ સાથે શેર કરી શકતાં નથી"
+ "આને ઑફિસ માટેના ઍપ સાથે શેર કરી શકતાં નથી""તમારા IT વ્યવસ્થાપક તમને તમારી કાર્યાલયની પ્રોફાઇલમાંની ઍપ વડે આ કન્ટેન્ટ શેર કરવાની મંજૂરી આપતા નથી""ઑફિસ માટેની ઍપ વડે આને ખોલી શકતાં નથી"
- "તમારા IT વ્યવસ્થાપક તમને તમારી કાર્યાલયની પ્રોફાઇલમાંની ઍપ વડે આ કન્ટેન્ટ ખોલવાની મંજૂરી આપતા નથી"
+ "તમારા IT વ્યવસ્થાપક તમને તમારી ઑફિસની પ્રોફાઇલમાંની ઍપ વડે આ કન્ટેન્ટ ખોલવાની મંજૂરી આપતા નથી""આને વ્યક્તિગત ઍપ સાથે શેર કરી શકતાં નથી""તમારા IT વ્યવસ્થાપક તમને તમારી વ્યક્તિગત પ્રોફાઇલમાંની ઍપ વડે આ કન્ટેન્ટ શેર કરવાની મંજૂરી આપતા નથી""વ્યક્તિગત ઍપ વડે આને ખોલી શકતાં નથી"
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6fd163153b0ee258bc6b24ece1bec3b237910045..59da793851b9830edccea080d0e1727476df9a37 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -184,7 +184,7 @@
"किसी अज्ञात तृतीय पक्ष के द्वारा""आपकी वर्क प्रोफ़ाइल का व्यवस्थापक करता है""%s के द्वारा"
- "वर्क प्रोफ़ाइल हटाई गई"
+ "वर्क प्रोफ़ाइल मिटाई गई""वर्क प्रोफ़ाइल व्यवस्थापक ऐप्लिकेशन या तो मौजूद नहीं है या वह खराब हो गया है. परिणामस्वरूप, आपकी वर्क प्रोफ़ाइल और उससे जुड़े डेटा को हटा दिया गया है. सहायता के लिए अपने व्यवस्थापक से संपर्क करें.""आपकी वर्क प्रोफ़ाइल अब इस डिवाइस पर उपलब्ध नहीं है""कई बार गलत पासवर्ड डाला गया"
@@ -202,10 +202,8 @@
"%s ने प्रिंटिंग सुविधा बंद कर दी है.""अपनी वर्क प्रोफ़ाइल चालू करें""निजी ऐप्लिकेशन अनब्लॉक करने के लिए, अपनी वर्क प्रोफ़ाइल चालू करें"
-
-
-
-
+ "निजी ऐप्लिकेशन %1$s को %2$s पर ब्लॉक कर दिए जाएंगे. आपका आईटी एडमिन आपकी वर्क प्रोफ़ाइल को %3$d दिन से ज़्यादा बंद रखने की अनुमति नहीं देता."
+ "चालू करें""मैं""टैबलेट विकल्प""Android TV डिवाइस में फ़ोन से जुड़े विकल्प"
@@ -1550,7 +1548,7 @@
"ब्राउज़र लॉन्च करें?""कॉल स्वीकार करें?""हमेशा"
- "केवल एक बार"
+ "सिर्फ़ एक बार""%1$s वर्क प्रोफ़ाइल का समर्थन नहीं करता""टैबलेट""टीवी"
@@ -1621,7 +1619,6 @@
"आपने अपने अनलॉक आकार को %1$d बार गलत तरीके से आरेखित किया है. %2$d और असफल प्रयासों के बाद, आपसे अपने फ़ोन को किसी ईमेल खाते का उपयोग करके अनलॉक करने के लिए कहा जाएगा.\n\n %3$d सेकंड में फिर से प्रयास करें."" — ""निकालें"
- "%1$sसे बैकग्राउंड में शुरू की गई फ़ॉरग्राउंड सेवा के लिए, भविष्य के R बिल्ड में \'इस्तेमाल के समय अनुमति दें\' की सुविधा नहीं होगी. कृपया go/r-bg-fgs-restriction देखें और गड़बड़ी की शिकायत करें.""वॉल्यूम को सुझाए गए स्तर से ऊपर बढ़ाएं?\n\nअत्यधिक वॉल्यूम पर ज़्यादा समय तक सुनने से आपकी सुनने की क्षमता को नुकसान हो सकता है.""सुलभता शॉर्टकट का इस्तेमाल करना चाहते हैं?""शॉर्टकट के चालू होने पर, दाेनाें वॉल्यूम बटन (आवाज़ कम या ज़्यादा करने वाले बटन) को तीन सेकंड तक दबाने से, सुलभता सुविधा शुरू हाे जाएगी."
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index c7ec2be91752e510f10900719ed8b3e7368b1f0f..5076aa94b1ea6b0d4ed0b0ba9ff808418bd3079b 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -204,10 +204,8 @@
"Ispis je onemogućila aplikacija %s.""Uključite poslovni profil""Vaše su osobne aplikacije blokirane dok ne uključite poslovni profil"
-
-
-
-
+ "Osobne aplikacije blokirat će se %1$s u %2$s. Vaš IT administrator ne dopušta da vaš poslovni profil ostane isključen dulje od %3$d dana."
+ "Uključi""Ja""Opcije tabletnog uređaja""Opcije Android TV-a"
@@ -241,7 +239,7 @@
"Opcije telefona""Zaključavanje zaslona""Isključi"
- "Napajanje"
+ "Uključi""Ponovo pokreni""Hitne službe""Izvješće o bugovima"
@@ -1150,7 +1148,7 @@
"Otvaranje pomoću aplikacije""Otvaranje pomoću aplikacije %1$s""Otvori"
- "Otvaranje veza s %1$s u aplikaciji"
+ "Otvaranje veza sa stranice %1$s u aplikaciji""Otvaranje veza u aplikaciji""Otvaranje veza u aplikaciji %1$s""Otvaranje veza s %1$s u aplikaciji %2$s"
@@ -1643,7 +1641,6 @@
"Netočno ste iscrtali obrazac za otključavanje %1$d puta. Nakon još ovoliko neuspješnih pokušaja: %2$d morat ćete otključati telefon pomoću računa e-pošte.\n\n Pokušajte ponovo za %3$d s."" – ""Ukloni"
- "Usluga u prednjem planu s %1$s pokrenuta u pozadini neće imati dopuštenje tijekom upotrebe u budućim R kompilacijama. Pogledajte go/r-bg-fgs-restriction i pošaljite izvješće o programskoj pogrešci.""Želite li pojačati zvuk iznad preporučene razine?\n\nDugotrajno slušanje glasne glazbe može vam oštetiti sluh.""Želite li upotrebljavati prečac za pristupačnost?""Kad je taj prečac uključen, pritiskom na obje tipke za glasnoću na tri sekunde pokrenut će se značajka pristupačnosti."
@@ -1669,7 +1666,7 @@
"Odabir značajki za upotrebu pomoću gumba za Pristupačnost""Odabir značajki za upotrebu pomoću prečaca tipki za glasnoću""Usluga %s je isključena"
- "Uredi prečace"
+ "Uredite prečace""Gotovo""Isključi prečac""Upotrijebi prečac"
@@ -2091,14 +2088,14 @@
"Posao""Osobni prikaz""Poslovni prikaz"
- "To se ne može dijeliti pomoću poslovnih aplikacija"
- "Vaš IT administrator ne dopušta vam dijeljenje tog sadržaja pomoću aplikacija s vašeg poslovnog profila"
- "To se ne može otvoriti pomoću poslovnih aplikacija"
- "Vaš IT administrator ne dopušta vam otvaranje tog sadržaja pomoću aplikacija s vašeg poslovnog profila"
+ "Nije moguće dijeljenje s poslovnim aplikacijama"
+ "Vaš IT administrator ne dopušta vam dijeljenje tog sadržaja s aplikacijama na vašem poslovnom profilu"
+ "Nije moguće otvaranje pomoću poslovnih aplikacija"
+ "Vaš IT administrator ne dopušta vam otvaranje tog sadržaja pomoću aplikacija na vašem poslovnom profilu""To se ne može dijeliti pomoću osobnih aplikacija""Vaš IT administrator ne dopušta vam dijeljenje tog sadržaja pomoću aplikacija s vašeg osobnog profila"
- "To se ne može otvoriti pomoću osobnih aplikacija"
- "Vaš IT administrator ne dopušta vam otvaranje tog sadržaja pomoću aplikacija s vašeg osobnog profila"
+ "Nije moguće otvaranje pomoću osobnih aplikacija"
+ "Vaš IT administrator ne dopušta vam otvaranje tog sadržaja pomoću aplikacija na vašem osobnom profilu""Poslovni je profil pauziran""Uključi""Nijedna poslovna aplikacija ne može podržati taj sadržaj"
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 29bb05250ecef58c7fa5de7eb4e93286a9633cab..6b442137da1888f93856f8fe0a8d81a2bb59c870 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -202,10 +202,8 @@
"A(z) %s letiltotta a nyomtatást.""Munkaprofil bekapcsolása""A személyes alkalmazások le lesznek tiltva, amíg be nem kapcsolja a munkaprofilt"
-
-
-
-
+ "A rendszer a következő időpontban letiltja a személyes alkalmazásokat: %1$s, %2$s. Rendszergazdája nem engedélyezi, hogy a munkaprofil %3$d napnál tovább kikapcsolva legyen."
+ "Bekapcsolás""Saját""Táblagép beállításai""Android TV beállításai"
@@ -1621,7 +1619,6 @@
"%1$d alkalommal helytelenül rajzolta le a feloldási mintát. További %2$d sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a telefonját.\n\n Kérjük, próbálja újra %3$d másodperc múlva."" – ""Eltávolítás"
- "A háttér által indított előtérben futó szolgáltatás (innen: %1$s) nem tartalmaz majd használat közbeni engedélyt a jövőbeli R buildekben. Kérjük, keresse fel a go/r-bg-fgs-restriction webhelyet, és jelentse a hibát.""Az ajánlott szint fölé szeretné emelni a hangerőt?\n\nHa hosszú időn át teszi ki magát nagy hangerőnek, azzal károsíthatja a hallását.""Szeretné használni a Kisegítő lehetőségek billentyűparancsot?""Ha a gyorsparancs aktív, akkor a két hangerőgomb három másodpercig tartó együttes lenyomásával kisegítő funkciót indíthat el."
@@ -1630,7 +1627,7 @@
" • %1$s\n""Bekapcsolja a következőt: %1$s?""A(z) %1$s kisegítő lehetőség bekapcsolásához tartsa nyomva néhány másodpercig mindkét hangerőgombot. Ez hatással lehet az eszköz működésére.\n\nEzt a gyorsparancsot a Beállítások > Kisegítő lehetőségek pontban módosíthatja másik funkció használatára."
- "Bekapcsolás"
+ "Bekapcsolom""Nem kapcsolom be""BE""KI"
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index ad5ae22a0f38cac57b197627340b62322212e96e..6f676fd7af80bf8919ec2c64a7b8a342b7924328 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -202,10 +202,8 @@
"Տպումն անջատված է %s հավելվածի կողմից։""Միացրեք աշխատանքային պորֆիլը""Անձնական հավելվածներն արգելափակված կլինեն, մինչև չմիացնեք ձեր աշխատանքային պրոֆիլը"
-
-
-
-
+ "Անձնական հավելվածները %1$s-ին ժամը %2$s կարգելափակվեն։ Ձեր ՏՏ ադմինիստրատորը չի թույլատրում, որ ձեր աշխատանքային պրոֆիլը %3$d օրից ավել անջատված մնա։"
+ "Միացնել""Իմ""Պլանշետի ընտրանքները""Android TV-ի կարգավորումներ"
@@ -1131,7 +1129,7 @@
"Բացել հավելվածով՝ %1$s""Բացել""%1$s տեսակի հղումները բացել…"
- "Հղումները բացել…"
+ "Հղումները բացել այս հավելվածով…""Հղումները բացել %1$s դիտարկիչում""%1$s տեսակի հղումները բացել %2$s դիտարկիչում""Թույլատրել"
@@ -1621,7 +1619,6 @@
"Դուք %1$d անգամ սխալ եք հավաքել ձեր ապակողպման նմուշը: %2$d անգամից ավել անհաջող փորձերից հետո ձեզ կառաջարկվի ապակողպել ձեր հեռախոսը` օգտագործելով էլփոստի հաշիվ:\n\n Փորձեք կրկին %3$d վայրկյանից:"" — ""Հեռացնել"
- "Ֆոնային ռեժիմում %1$s-ից գործարկված առաջին պլանի ծառայությունը հետագա R կառուցումներում թույլտվություն չի ունենա օգտագործման ընթացքում։ Անցեք go/r-bg-fgs-restriction էջ և հաղորդեք վրիպակի մասին։""Ձայնը բարձրացնե՞լ խորհուրդ տրվող մակարդակից ավել:\n\nԵրկարատև բարձրաձայն լսելը կարող է վնասել ձեր լսողությունը:""Օգտագործե՞լ Մատչելիության դյուրանցումը։""Հատուկ գործառույթն օգտագործելու համար սեղմեք և 3 վայրկյան սեղմած պահեք ձայնի ուժգնության երկու կոճակները, երբ գործառույթը միացված է:"
@@ -1843,7 +1840,7 @@
"Աշխատանքային օր""Շաբաթ-կիրակի""Միջոցառում"
- "Քնելիս"
+ "Քնի ժամանակ""%1$s-ն անջատում է որոշ ձայներ""Սարքում ներքին խնդիր է առաջացել և այն կարող է կրկնվել, մինչև չվերականգնեք գործարանային կարգավորումները:""Սարքում ներքին խնդիր է առաջացել: Մանրամասների համար կապվեք արտադրողի հետ:"
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f031ed8e5ea2b8c90e0bbd5d8f51c7a3b9fdf996..3aaf2f5f6404bde2b1a66d8fba7e5233fbbcb635 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -202,10 +202,8 @@
"Fitur pencetakan dinonaktifkan oleh %s.""Aktifkan profil kerja Anda""Aplikasi pribadi Anda diblokir hingga Anda mengaktifkan profil kerja Anda"
-
-
-
-
+ "Aplikasi pribadi akan diblokir pada tanggal %1$s pukul %2$s. Admin IT tidak mengizinkan profil kerja Anda nonaktif selama lebih dari %3$d hari."
+ "Aktifkan""Saya""Opsi tablet""Opsi Android TV"
@@ -341,7 +339,7 @@
"Mengizinkan aplikasi menambahkan pintasan Layar Utama tanpa tindakan dari pengguna.""meng-uninstal pintasan""Mengizinkan aplikasi menghapus pintasan Layar Utama tanpa tindakan dari pengguna."
- "ubah rute panggilan keluar"
+ "alihkan panggilan keluar""Memungkinkan aplikasi melihat nomor yang dihubungi saat melakukan panggilan keluar dengan opsi untuk mengalihkan panggilan ke nomor lain atau membatalkan panggilan sepenuhnya.""jawab panggilan telepon""Mengizinkan aplikasi menjawab panggilan telepon masuk."
@@ -1621,7 +1619,6 @@
"Anda telah %1$d kali salah menggambar pola pembuka kunci. Setelah %2$d lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan akun email.\n\nCoba lagi dalam %3$d detik."" — ""Hapus"
- "Layanan latar depan yang dimulai oleh latar belakang dari %1$s tidak akan memiliki izin saat-sedang-digunakan pada build R masa mendatang Lihat go/r-bg-fgs-restriction dan kirim laporan bug.""Mengeraskan volume di atas tingkat yang disarankan?\n\nMendengarkan dengan volume keras dalam waktu yang lama dapat merusak pendengaran Anda.""Gunakan Pintasan Aksesibilitas?""Saat pintasan aktif, menekan kedua tombol volume selama 3 detik akan memulai fitur aksesibilitas."
@@ -1653,8 +1650,8 @@
"Gunakan Pintasan""Inversi Warna""Koreksi Warna"
- "Tombol volume yang ditahan. %1$s diaktifkan."
- "Tombol volume yang ditahan. %1$s dinonaktifkan."
+ "Tombol volume ditahan. %1$s diaktifkan."
+ "Tombol volume ditahan. %1$s dinonaktifkan.""Tekan dan tahan kedua tombol volume selama tiga detik untuk menggunakan %1$s""Pilih fitur yang akan digunakan saat mengetuk tombol aksesibilitas:""Pilih fitur yang akan digunakan dengan gestur aksesibilitas (geser ke atas dari bawah layar dengan dua jari):"
@@ -1901,7 +1898,7 @@
"Beberapa fitur tidak dapat digunakan""Profil kerja terkunci""Ketuk untuk membuka kunci profil kerja"
- "Tersambung ke %1$s"
+ "Terhubung ke %1$s""Ketuk untuk melihat file""Pasang pin""Pasang pin %1$s"
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index c16e1d11608dc33e6338194d50e6e47a9a98ac8f..e5e23f6e0ac3f557e437d5e0af1bc85501ad50ff 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -202,10 +202,8 @@
"%s lokaði á prentun.""Kveiktu á vinnusniði""Lokað er á forrit til einkanota þar til þú kveikir á vinnusniði"
-
-
-
-
+ "Lokað verður á forrit til einkanota %1$s kl. %2$s. Kerfisstjórinn þinn leyfir ekki að slökkt sé á vinnusniðinu í meira en %3$d daga."
+ "Kveikja""Ég""Valkostir spjaldtölvu""Valkostir Android TV"
@@ -239,7 +237,7 @@
"Valkostir síma""Skjálás""Slökkva"
- "Orka"
+ "Slökkva/endurræsa""Endurræsa""Neyðarsímtal""Villutilkynning"
@@ -1621,7 +1619,6 @@
"Þú hefur teiknað rangt opnunarmynstur %1$d sinnum. Eftir %2$d árangurslausar tilraunir í viðbót verður þú beðin(n) um að opna símann með tölvupóstreikningi.\n\n Reyndu aftur eftir %3$d sekúndur."" — ""Fjarlægja"
- "Forgrunnsþjónusta frá %1$s sem er ræst úr bakgrunni mun ekki hafa heimild við notkun í framtíðarútgáfum R. Farðu á go/r-bg-fgs-restriction og gefðu villuskýrslu.""Hækka hljóðstyrk umfram ráðlagðan styrk?\n\nEf hlustað er á háum hljóðstyrk í langan tíma kann það að skaða heyrnina.""Viltu nota aðgengisflýtileið?""Þegar flýtileiðin er virk er kveikt á aðgengiseiginleikanum með því að halda báðum hljóðstyrkshnöppunum inni í þrjár sekúndur."
@@ -1645,7 +1642,7 @@
"Hafna""Ýttu á eiginleika til að byrja að nota hann:""Veldu eiginleika sem á að nota með aðgengishnappinum"
- "Veldu eiginleika á að nota með flýtileið hljóðstyrkstakka"
+ "Veldu eiginleika sem á að nota með flýtileið hljóðstyrkstakka""Slökkt hefur verið á %s""Breyta flýtileiðum""Lokið"
@@ -2032,7 +2029,7 @@
%s + %d skrá%s + %d skrá
- "Engar tillögur um einstaklinga til að deila með"
+ "Engar tillögur um fólk til að deila með""Forritalisti""Þetta forrit hefur ekki fengið heimild fyrir upptöku en gæti tekið upp hljóð í gegnum þetta USB-tæki.""Heim"
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 61983db358c3c1849d87a5de20ecfd0f844581c8..de4de72b0e6c26804e0d78263ceb29fa4b8d4ff0 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -201,11 +201,9 @@
"Impossibile usare l\'app di amministrazione. Il dispositivo verrà resettato.\n\nPer eventuali domande, contatta l\'amministratore della tua organizzazione.""Stampa disattivata da %s.""Attiva il profilo di lavoro"
- "Le tue app personali sono bloccate fino all\'attivazione del tuo profilo di lavoro"
-
-
-
-
+ "Le tue app personali saranno bloccate finché non attivi il tuo profilo di lavoro."
+ "Le app personali verranno bloccate il giorno %1$s alle ore %2$s. L\'amministratore IT non consente di mantenere disattivato il profilo di lavoro per più di %3$d giorni."
+ "Attiva""Io""Opzioni tablet""Opzioni Android TV"
@@ -224,7 +222,7 @@
"Riavvio in corso…""Ripristino dati di fabbrica""Riavvio in corso…"
- "Spegnimento..."
+ "Spegnimento…""Il tablet verrà spento.""Il dispositivo Android TV verrà spento.""L\'orologio verrà spento."
@@ -858,7 +856,7 @@
"La SIM è bloccata tramite PUK.""Consulta la Guida dell\'utente o contatta il servizio clienti.""La SIM è bloccata."
- "Sblocco SIM..."
+ "Sblocco SIM…""%1$d tentativi errati di inserimento della sequenza di sblocco. \n\nRiprova tra %2$d secondi.""Hai digitato la tua password %1$d volte in modo errato. \n\nRiprova tra %2$d secondi.""Hai digitato il tuo PIN %1$d volte in modo errato. \n\nRiprova tra %2$d secondi."
@@ -881,7 +879,7 @@
"Accedi""Password o nome utente non valido.""Hai dimenticato il nome utente o la password?\nVisita ""google.com/accounts/recovery""."
- "Verifica..."
+ "Verifica…""Sblocca""Audio attivato""Audio disattivato"
@@ -1097,7 +1095,7 @@
"Impossibile copiare negli appunti""Incolla""Incolla come testo normale"
- "Sostituisci..."
+ "Sostituisci…""Elimina""Copia URL""Seleziona testo"
@@ -1119,7 +1117,7 @@
"OK""Annulla""Attenzione"
- "Caricamento..."
+ "Caricamento…""ON""OFF""selezionato"
@@ -1546,7 +1544,7 @@
"Mostra tutto""Scegli attività""Condividi con"
- "Invio..."
+ "Invio…""Avviare l\'applicazione Browser?""Accettare la chiamata?""Sempre"
@@ -1593,7 +1591,7 @@
"La scheda SIM è disattivata. Inserisci il codice PUK per continuare. Contatta l\'operatore per avere informazioni dettagliate.""Inserisci il codice PIN desiderato""Conferma il codice PIN desiderato"
- "Sblocco scheda SIM..."
+ "Sblocco scheda SIM…""Codice PIN errato.""Il PIN deve essere di 4-8 numeri.""Il codice PUK deve essere di 8 cifre."
@@ -1621,12 +1619,11 @@
"%1$d tentativi errati di inserimento della sequenza di sblocco. Dopo altri %2$d tentativi falliti, ti verrà chiesto di sbloccare il telefono con un account email.\n\n Riprova tra %3$d secondi."" – ""Rimuovi"
- "Il servizio in primo piano avviato in background da %1$s non avrà l\'autorizzazione \"durante l\'uso\" nelle future build R. Visita la pagina go/r-bg-fgs-restriction e invia una segnalazione di bug.""Vuoi aumentare il volume oltre il livello consigliato?\n\nL\'ascolto ad alto volume per lunghi periodi di tempo potrebbe danneggiare l\'udito.""Usare la scorciatoia Accessibilità?""Quando la scorciatoia è attiva, puoi premere entrambi i pulsanti del volume per tre secondi per avviare una funzione di accessibilità.""Attivare le funzioni di accessibilità?"
- "Se tieni premuti entrambi i tasti del volume per qualche secondo, le funzioni di accessibilità vengono attivate. Questa operazione potrebbe modificare il funzionamento del dispositivo.\n\nFunzionalità correnti:\n%1$s\nPuoi modificare le funzionalità selezionate in Impostazioni > Accessibilità."
+ "Se tieni premuti entrambi i tasti del volume per qualche secondo, vengono attivate le funzioni di accessibilità. Questa operazione potrebbe modificare il funzionamento del dispositivo.\n\nFunzioni correnti:\n%1$s\nPuoi modificare le funzioni selezionate in Impostazioni > Accessibilità."" • %1$s\n""Attivare %1$s?""Se tieni premuti entrambi i tasti del volume per qualche secondo verrà attivata la funzione di accessibilità %1$s. Questa operazione potrebbe modificare il funzionamento del dispositivo.\n\nPuoi associare questa scorciatoia a un\'altra funzionalità in Impostazioni > Accessibilità."
@@ -1645,7 +1642,7 @@
"Rifiuta""Tocca una funzionalità per iniziare a usarla:""Scegli le funzionalità da usare con il pulsante Accessibilità"
- "Scegli le funzionalità da usare con la scorciatoia per i tasti del volume"
+ "Scegli le funzionalità da usare con la scorciatoia dei tasti del volume""Il servizio %s è stato disattivato""Modifica scorciatoie""Fine"
@@ -2057,10 +2054,10 @@
"Lavoro""Visualizzazione personale""Visualizzazione di lavoro"
- "Impossibile condividere questi contenuti con app di lavoro"
- "L\'amministratore IT non ti consente di condividere questi contenuti con app nel tuo profilo di lavoro"
- "Impossibile aprire questi contenuti con app di lavoro"
- "L\'amministratore IT non ti consente di aprire questi contenuti con app nel tuo profilo di lavoro"
+ "Impossibile condividere questi contenuti con le app di lavoro"
+ "L\'amministratore IT non ti consente di condividere questi contenuti con le app nel tuo profilo di lavoro"
+ "Impossibile aprire questi contenuti con le app di lavoro"
+ "L\'amministratore IT non ti consente di aprire questi contenuti con le app nel tuo profilo di lavoro""Impossibile condividere questi contenuti con app personali""L\'amministratore IT non ti consente di condividere questi contenuti con app nel tuo profilo personale""Impossibile aprire questi contenuti con app personali"
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 92bde45559d8292012e1c92321ce351d2a1a7ab5..1b3fb1ba087aa551d043be5e8f33a21d5238a247 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -206,10 +206,8 @@
"ההדפסה הושבתה על ידי %s.""הפעלה של פרופיל העבודה שלך""האפליקציות שלך לשימוש אישי יהיו חסומות עד להפעלת פרופיל העבודה"
-
-
-
-
+ "אפליקציות לשימוש אישי ייחסמו ב-%1$s בשעה %2$s. מנהל ה-IT לא מתיר השבתה של יותר מ-%3$d ימים של פרופיל העבודה."
+ "הפעלה""אני""אפשרויות טאבלט""אפשרויות Android TV"
@@ -1665,7 +1663,6 @@
"שרטטת את קו ביטול הנעילה באופן שגוי %1$d פעמים. לאחר %2$d ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלפון באמצעות חשבון אימייל.\n\nנסה שוב בעוד %3$d שניות."" — ""הסר"
- "לשירות שפועל בחזית מ-%1$s שהחל ברקע לא תהיה הרשאת while-in-use בגרסאות R build בעתיד. יש לעיין בכתובת go/r-bg-fgs-restriction ולהגיש דוח על באג.""האם להעלות את עוצמת הקול מעל לרמה המומלצת?\n\nהאזנה בעוצמת קול גבוהה למשכי זמן ממושכים עלולה לפגוע בשמיעה.""להשתמש בקיצור הדרך לתכונת הנגישות?""כשקיצור הדרך מופעל, לחיצה על שני לחצני עוצמת הקול למשך שלוש שניות מפעילה את תכונת הנגישות."
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 540f01f5e1b107acf2c52f6f9d34b3fbfb0aee72..1604bea1e763dec551824b6293c43e2872e296d1 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -202,10 +202,8 @@
"「%s」により印刷は無効にされています。""仕事用プロファイルを ON にする""個人用アプリは、仕事用プロファイルを ON にしない限りブロックされます"
-
-
-
-
+ "個人用アプリは、%1$s%2$s にブロックされます。仕事用プロファイルを %3$d 日を超えて OFF にすることは、IT 管理者から許可されていません。"
+ "ON にする""自分""タブレットオプション""Android TV のオプション"
@@ -1151,7 +1149,7 @@
"「%1$s」を使用して画像をキャプチャ""画像をキャプチャ""常にこの操作で使用する"
- "別のアプリの使用"
+ "別のアプリを使用""[システム設定]>[アプリ]>[ダウンロード済み]でデフォルト設定をクリアします。""操作の選択""USBデバイス用アプリを選択"
@@ -1313,7 +1311,7 @@
"USB デバッグを無効にする場合に選択します。""ワイヤレス デバッグが接続されました""ワイヤレス デバッグをUSB デバッグを無効にするにはここをタップしてください"
- "ワイヤレス デバッグを無効にする場合に選択します。"
+ "ワイヤレス デバッグを無効にするには選択します。""テストハーネス モード有効""出荷時設定にリセットしてテストハーネス モードを無効にしてください。""シリアル コンソールは有効です"
@@ -1550,7 +1548,7 @@
"ブラウザを起動しますか?""通話を受けますか?""常時"
- "1回のみ"
+ "1 回のみ""%1$sは仕事用プロファイルをサポートしていません""タブレット""テレビ"
@@ -1621,7 +1619,6 @@
"ロック解除パターンの入力を%1$d回間違えました。あと%2$d回間違えると、モバイルデバイスのロック解除にメールアカウントが必要になります。\n\n%3$d秒後にもう一度お試しください。"" - ""削除"
- "今後の R ビルドでは、%1$s からバックグラウンドで開始されるフォアグラウンド サービスに「使用中のみ許可」の権限がありません。go/r-bg-fgs-restriction を確認し、バグレポートを提出してください。""推奨レベルを超えるまで音量を上げますか?\n\n大音量で長時間聞き続けると、聴力を損なう恐れがあります。""ユーザー補助機能のショートカットの使用""ショートカットが ON の場合、両方の音量ボタンを 3 秒ほど長押しするとユーザー補助機能が起動します。"
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 41d42e7733b515e4fb32664cbe1c751a4e7ea0dc..3d447fc00bf0edf828ed65ebe9df0ee5a1f10716 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -202,10 +202,8 @@
"ბეჭდვა გათიშულია %s-ის მიერ.""ჩართეთ სამსახურის პროფილი""თქვენი პირადი აპები დაბლოკილი იქნება, სანამ სამსახურის პროფილს არ ჩართავთ"
-
-
-
-
+ "პირადი აპები დაიბლოკება: %1$s, %2$s. თქვენი IT ადმინისტრატორი თქვენს სამსახურის პროფილს არ აძლევს უფლებას, გამორთული იყოს %3$d დღეზე მეტ ხანს."
+ "ჩართვა""მე""ტაბლეტის პარამეტრები""Android TV ვარიანტები"
@@ -1621,7 +1619,6 @@
"თქვენ არასწორად დახატეთ თქვენი განბლოკვის ნიმუში %1$d-ჯერ. კიდევ %2$d წარუმატებელი ცდის შემდეგ, დაგჭირდებათ თქვენი ტელეფონის განბლოკვა ელფოსტის ანგარიშის გამოყენებით.\n\n ხელახლა სცადეთ %3$d წამში."" — ""ამოშლა"
- "%1$s-ის ფონურად დაწყებულ წინა პლანის სერვისს მომავალ R build-ებში გამოყენების პროცესში წვდომის ნებართვა არ ექნება. გთხოვთ, იხილოთ go/r-bg-fgs-restriction და გამოგზავნოთ ხარვეზის ანგარიში.""გსურთ ხმის რეკომენდებულ დონეზე მაღლა აწევა?\n\nხანგრძლივად ხმამაღლა მოსმენით შესაძლოა სმენადობა დაიზიანოთ.""გსურთ მარტივი წვდომის მალსახმობის გამოყენება?""თუ მალსახმობი ჩართულია, ხმის ორივე ღილაკზე 3 წამის განმავლობაში დაჭერით მარტივი წვდომის ფუნქცია ჩაირთვება."
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 7cee31ab6ea7d00b6563c98c03295f9d6e697324..c55946e88ae9a8d8d196fb19bdcc74f3b02bd680 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -202,10 +202,8 @@
"Басып шығаруды %s өшірді.""Жұмыс профиліңізді қосыңыз""Жұмыс профиліңізді қоспайынша, жеке қолданбалар бөгеледі."
-
-
-
-
+ "Жеке қолданбалардың бөгелетін уақыты: %1$s, сағат %2$s. Әкімші жұмыс профилін %3$d күннен аса мерзімге өшіруге рұқсат бермейді."
+ "Қосу""Мен""Планшет опциялары""Android TV опциялары"
@@ -1313,7 +1311,7 @@
"USB арқылы түзетуді өшіру үшін таңдаңыз.""Сымсыз түзету байланыстырылды""Сымсыз түзетуді өшіру үшін түртіңіз."
- "Сымсыз түзетуді өшіріңіз."
+ "Сымсыз түзетуді өшіру үшін басыңыз.""Сынақ бағдарламасы режимі қосылды""Сынақ бағдарламасы режимін өшіру үшін зауыттық күйіне қайтарыңыз.""Сериялық консоль қосылды"
@@ -1549,7 +1547,7 @@
"Жіберілуде...""Браузер қосылсын ба?""Қоңырауды қабылдау?"
- "Үнемі"
+ "Әрқашан""Бір рет қана""%1$s жұмыс профилін қолдамайды""Планшет"
@@ -1621,12 +1619,11 @@
"Бекітпені ашу кескінін %1$d рет қате сыздыңыз. %2$d сәтсіз әрекеттен кейін телефоныңызды есептік жазба арқылы ашу өтінішін аласыз. \n\n %3$d секундтан кейін қайта әрекеттеніңіз."" — ""Алып тастау"
- "Фондық режимде іске қосылған %1$s белсенді пакетінің алдағы R құрамаларында \"Пайдаланғанда ғана рұқсат ету\" рұқсаты болмайды. go/r-bg-fgs-restriction бетіне өтіп, қате туралы есеп жіберіңіз.""Дыбыс деңгейін ұсынылған деңгейден көтеру керек пе?\n\nЖоғары дыбыс деңгейінде ұзақ кезеңдер бойы тыңдау есту қабілетіңізге зиян тигізуі мүмкін.""Арнайы мүмкіндік төте жолын пайдалану керек пе?""Түймелер тіркесімі қосулы кезде, екі дыбыс түймесін 3 секунд басып тұрсаңыз, \"Арнайы мүмкіндіктер\" функциясы іске қосылады.""Арнайы мүмкіндіктер іске қосылсын ба?"
- "Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, арнайы мүмкіндіктер іске қосылады. Бұл – құрылғының жұмысына әсер етуі мүмкін.\n\nФункциялар:\n%1$s\nТаңдалған функцияларды \"Параметрлер > Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."
+ "Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, арнайы мүмкіндіктер іске қосылады. Бұл – құрылғының жұмысына әсер етуі мүмкін.\n\nҚазіргі функциялар:\n%1$s\nТаңдалған функцияларды \"Параметрлер > Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."" • %1$s\n""%1$s қосылсын ба?""Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, %1$s арнайы қызметі іске қосылады. Бұл – құрылғының жүмысына әсер етуі мүмкін.\n\nБұл таңбашаны басқа функцияға \"Параметрлер > Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."
@@ -1645,16 +1642,16 @@
"Қабылдамау""Функцияны пайдалана бастау үшін түртіңіз:""\"Арнайы мүмкіндіктер\" түймесімен қолданылатын функцияларды таңдаңыз"
- "Дыбыс деңгейі пернелері таңбашасымен қолданылатын функцияларды таңдаңыз"
+ "Дыбыс деңгейі пернелері тіркесімімен қолданылатын функцияларды таңдаңыз""%s қызметі өшірулі."
- "Таңбашаларды өзгерту"
+ "Жылдам пәрмендерді өзгерту""Дайын""Төте жолды өшіру""Төте жолды пайдалану""Түстер инверсиясы""Түсті түзету""Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. %1$s қосулы."
- "Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. %1$s өшірулі."
+ "Дыбыс деңгейі пернелерін басып тұрған соң, %1$s өшірілді.""%1$s қызметін пайдалану үшін дыбыс деңгейін реттейтін екі түймені де 3 секунд басып тұрыңыз""\"Арнайы мүмкіндіктер\" түймесін түрткенде пайдаланатын функцияны таңдаңыз:""Арнайы мүмкіндіктер қимылымен (екі саусақпен экранның төменгі жағынан жоғары қарай сырғытыңыз) пайдаланатын функцияны таңдаңыз:"
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 3fe0766956422a38fa1292c658284e73abd96279..28629fd3bd6528b69922f1c88f2bb24b334a7f3a 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -202,10 +202,8 @@
"ការបោះពុម្ពត្រូវបានបិទដោយ %s ។""បើកកម្រងព័ត៌មានការងាររបស់អ្នក""កម្មវិធីផ្ទាល់ខ្លួនរបស់អ្នកត្រូវបានទប់ស្កាត់ រហូតទាល់តែអ្នកបើកកម្រងព័ត៌មានការងាររបស់អ្នក"
-
-
-
-
+ "កម្មវិធីផ្ទាល់ខ្លួននឹងត្រូវបានទប់ស្កាត់នៅថ្ងៃទី %1$s នៅម៉ោង %2$s។ អ្នកគ្រប់គ្រងផ្នែកព័ត៌មានវិទ្យារបស់អ្នកមិនអនុញ្ញាតឱ្យបន្តបិទកម្រងព័ត៌មានការងាររបស់អ្នកលើសពី %3$d ថ្ងៃទេ។"
+ "បើក""ខ្ញុំ""ជម្រើសកុំព្យូទ័របន្ទះ""ជម្រើស Android TV"
@@ -1125,17 +1123,15 @@
"បានធីក""មិនបានធីក""បញ្ចប់សកម្មភាពដោយប្រើ"
-
-
-
+ "បញ្ចប់សកម្មភាពដោយប្រើ %1$s""បញ្ចប់សកម្មភាព""បើកជាមួយ""បើកជាមួយ %1$s""បើក"
- "បើកតំណ %1$s ជាមួយ"
- "បើកតំណជាមួយ"
- "បើកតំណជាមួយ %1$s"
- "បើកតំណ %1$s ជាមួយ %2$s"
+ "បើកតំណ %1$s ដោយប្រើ"
+ "បើកតំណដោយប្រើ"
+ "បើកតំណដោយប្រើ %1$s"
+ "បើកតំណ %1$s ដោយប្រើ %2$s""ផ្តល់សិទ្ធិចូលប្រើ""កែសម្រួលជាមួយ""កែសម្រួលជាមួយ %1$s"
@@ -1230,7 +1226,7 @@
"កម្រិតសំឡេង""កម្រិតសំឡេងប៊្លូធូស""កម្រិតសំឡេងរោទ៍"
- "កម្រិតសំឡេងហៅ"
+ "កម្រិតសំឡេងហៅទូរសព្ទ""កម្រិតសំឡេងមេឌៀ""កម្រិតសំឡេងការជូនដំណឹង""សំឡេងរោទ៍លំនាំដើម"
@@ -1623,7 +1619,6 @@
"អ្នកបានគូរលំនាំដោះសោរបស់អ្នកមិនត្រឹមត្រូវចំនួន %1$d ដង។ បន្ទាប់ពីការព្យាយាមមិនជោគជ័យច្រើនជាង %2$d ដង អ្នកនឹងត្រូវបានស្នើឲ្យដោះសោទូរស័ព្ទរបស់អ្នកដោយប្រើគណនីអ៊ីមែល។\n\n ព្យាយាមម្ដងទៀតក្នុងរយៈពេល %3$d វិនាទី។"" — ""លុបចេញ"
- "សេវាកម្មផ្ទៃខាងមុខដែលចាប់ផ្ដើមដោយផ្ទៃខាងក្រោយពី %1$s នឹងមិនមានការអនុញ្ញាតខណៈពេលកំពុងប្រើប្រាស់ទេ នៅក្នុងកំណែបង្កើត R នៅពេលអនាគត។ សូមមើល go/r-bg-fgs-restriction និងផ្ញើរបាយការណ៍អំពីបញ្ហា។""បង្កើនកម្រិតសំឡេងលើសពីកម្រិតបានផ្ដល់យោបល់?\n\nការស្ដាប់នៅកម្រិតសំឡេងខ្លាំងយូរអាចធ្វើឲ្យខូចត្រចៀក។""ប្រើប្រាស់ផ្លូវកាត់ភាពងាយស្រួល?""នៅពេលបើកផ្លូវកាត់ ការចុចប៊ូតុងកម្រិតសំឡេងទាំងពីររយៈពេល 3 វិនាទីនឹងចាប់ផ្តើមមុខងារភាពងាយប្រើ។"
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index bc896d61a43b85f934e9667aa86fdf5db938164e..2f89ba370b39d374e7d482a10a849a2a3518c198 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -202,10 +202,8 @@
"%s ಮೂಲಕ ಪ್ರಿಂಟಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ.""ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ ಸಕ್ರಿಯಗೊಳಿಸಿ""ನಿಮ್ಮ ಉದ್ಯೋಗದ ಪ್ರೊಫೈಲ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುವವರೆಗೆ ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಆ್ಯಪ್ಗಳನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗುತ್ತದೆ"
-
-
-
-
+ "ವೈಯಕ್ತಿಕ ಆ್ಯಪ್ಗಳನ್ನು %1$s ರಂದು %2$s ಸಮಯಕ್ಕೆ ನಿರ್ಬಂಧಿಸಲಾಗುತ್ತದೆ. ನಿಮ್ಮ ಐಟಿ ನಿರ್ವಾಹಕರು ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್ ಅನ್ನು %3$d ದಿನಗಳಿಗಿಂತ ಹೆಚ್ಚು ಕಾಲ ಉಳಿಯಲು ಅನುಮತಿಸುವುದಿಲ್ಲ."
+ "ಆನ್""ನಾನು""ಟ್ಯಾಬ್ಲೆಟ್ ಆಯ್ಕೆಗಳು""Android TV ಆಯ್ಕೆಗಳು"
@@ -1308,7 +1306,7 @@
"ಸಂಪರ್ಕಗೊಂಡಿರುವ ಸಾಧನವನ್ನು ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ. ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ.""ಅನ್ಲಾಗ್ ಆಡಿಯೋ ಪರಿಕರ ಪತ್ತೆಯಾಗಿದೆ""ಲಗತ್ತಿಸಲಾದ ಸಾಧನವು ಈ ಫೋನಿನೊಂದಿಗೆ ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ. ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."
- "USB ಡೀಬಗಿಂಗ್ ಸಂಪರ್ಕಗೊಂಡಿದೆ"
+ "USB ಡೀಬಗ್ ಮಾಡುವಿಕೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ""USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ಆಫ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ""USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಆಯ್ಕೆ ಮಾಡಿ.""ವೈರ್ಲೆಸ್ ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"
@@ -1621,12 +1619,11 @@
"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ನೀವು %1$d ಬಾರಿ ತಪ್ಪಾಗಿ ಡ್ರಾ ಮಾಡಿರುವಿರಿ. %2$d ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ಬಳಿಕ, ನಿಮ್ಮ ಇಮೇಲ್ ಖಾತೆಯನ್ನು ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡುವಂತೆ ನಿಮ್ಮಲ್ಲಿ ಕೇಳಿಕೊಳ್ಳಲಾಗುತ್ತದೆ.\n\n %3$d ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."" — ""ತೆಗೆದುಹಾಕು"
- "%1$s ನಿಂದ ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಪ್ರಾರಂಭಿಸಲಾದ ಮುನ್ನೆಲೆ ಸೇವೆ ಭವಿಷ್ಯದ R ಬಿಲ್ಡ್ಗಳಿಂದ ಬಳಕೆಯಲ್ಲಿರುವಾಗ ಅನುಮತಿಯನ್ನು ಪಡೆಯುವುದಿಲ್ಲ. go/r-bg-fgs-restriction ಅನ್ನು ನೋಡಿ ಮತ್ತು ದೋಷವರದಿಯನ್ನು ಫೈಲ್ ಮಾಡಿ.""ವಾಲ್ಯೂಮ್ ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾದ ಮಟ್ಟಕ್ಕಿಂತಲೂ ಹೆಚ್ಚು ಮಾಡುವುದೇ?\n\nದೀರ್ಘ ಅವಧಿಯವರೆಗೆ ಹೆಚ್ಚಿನ ವಾಲ್ಯೂಮ್ನಲ್ಲಿ ಆಲಿಸುವುದರಿಂದ ನಿಮ್ಮ ಆಲಿಸುವಿಕೆ ಸಾಮರ್ಥ್ಯಕ್ಕೆ ಹಾನಿಯುಂಟು ಮಾಡಬಹುದು.""ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್ಕಟ್ ಬಳಸುವುದೇ?""ಶಾರ್ಟ್ಕಟ್ ಆನ್ ಆಗಿರುವಾಗ, ಎರಡೂ ವಾಲ್ಯೂಮ್ ಬಟನ್ಗಳನ್ನು 3 ಸೆಕೆಂಡುಗಳ ಕಾಲ ಒತ್ತಿದರೆ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯವೊಂದು ಪ್ರಾರಂಭವಾಗುತ್ತದೆ."
- "ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆನ್ ಮಾಡುವುದೇ?"
- "ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಕೆಲವು ಸೆಕೆಂಡುಗಳ ಕಾಲ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳು ಆನ್ ಆಗುತ್ತವೆ. ಇದು ನಿಮ್ಮ ಸಾಧನವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಬಹುದು.\n\n ಪ್ರಸ್ತುತ ವೈಶಿಷ್ಟ್ಯಗಳು:\n%1$s\nಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಅಕ್ಸೆಸಿಬಿಲಿಟಿಯಲ್ಲಿ ಆಯ್ದ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ನೀವು ಬದಲಾಯಿಸಬಹುದು."
+ "ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆನ್ ಮಾಡಬೇಕೇ?"
+ "ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಕೆಲವು ಸೆಕೆಂಡುಗಳ ಕಾಲ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳು ಆನ್ ಆಗುತ್ತವೆ. ಇದು ನಿಮ್ಮ ಸಾಧನವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಬಹುದು.\n\n ಪ್ರಸ್ತುತ ವೈಶಿಷ್ಟ್ಯಗಳು:\n%1$s\nಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿಯಲ್ಲಿ ಆಯ್ದ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ನೀವು ಬದಲಾಯಿಸಬಹುದು."" • %1$s\n""%1$s ಅನ್ನು ಆನ್ ಮಾಡುವುದೇ?""ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಕೆಲವು ಸೆಕೆಂಡುಗಳ ಕಾಲ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯವಾದ %1$s ಆನ್ ಆಗುತ್ತದೆ. ಇದು ನಿಮ್ಮ ಸಾಧನವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಬಹುದು.\n\nನೀವು ಈ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಅಕ್ಸೆಸಿಬಿಲಿಟಿಯಲ್ಲಿನ ಮತ್ತೊಂದು ವೈಶಿಷ್ಟ್ಯಕ್ಕೆ ಬದಲಾಯಿಸಬಹುದು."
@@ -1654,7 +1651,7 @@
"ಬಣ್ಣ ವಿಲೋಮ""ಬಣ್ಣ ತಿದ್ದುಪಡಿ""ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. %1$s ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."
- "ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. %1$s ಅನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ."
+ "ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಲಾಗಿದೆ. %1$s, ಆಫ್ ಮಾಡಲಾಗಿದೆ.""%1$s ಅನ್ನು ಬಳಸಲು ಎರಡೂ ಧ್ವನಿ ಕೀಗಳನ್ನು ಮೂರು ಸೆಕೆಂಡ್ಗಳ ಕಾಲ ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ""ನೀವು ಪ್ರವೇಶಿಸುವಿಕೆ ಬಟನ್ ಟ್ಯಾಪ್ ಮಾಡಿದಾಗ ಬಳಸುವುದಕ್ಕಾಗಿ ವೈಶಿಷ್ಟ್ಯವೊಂದನ್ನು ಆರಿಸಿ:""ಪ್ರವೇಶಿಸುವಿಕೆ ಗೆಸ್ಚರ್ನೊಂದಿಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯವೊಂದನ್ನು ಆಯ್ಕೆಮಾಡಿ (ಎರಡು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಪರದೆಯ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ):"
@@ -2032,7 +2029,7 @@
%s + %d ಫೈಲ್ಗಳು%s + %d ಫೈಲ್ಗಳು
- "ಹಂಚಿಕೊಳ್ಳಲು, ಯಾವುದೇ ಶಿಫಾರಸು ಮಾಡಲಾದ ಜನರಿಲ್ಲ"
+ "ಹಂಚಿಕೊಳ್ಳಲು ಶಿಫಾರಸು ಮಾಡಲಾದವರು ಯಾರೂ ಇಲ್ಲ""ಆ್ಯಪ್ಗಳ ಪಟ್ಟಿ""ಈ ಆ್ಯಪ್ಗೆ ರೆಕಾರ್ಡ್ ಅನುಮತಿಯನ್ನು ನೀಡಲಾಗಿಲ್ಲ, ಆದರೆ ಈ USB ಸಾಧನದ ಮೂಲಕ ಆಡಿಯೊವನ್ನು ಸೆರೆಹಿಡಿಯಬಲ್ಲದು.""ಹೋಮ್"
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 87ed01943aa844004b4c82460bf317d08c2ae8e9..63c3a379ad66a63592cf6bc024a3f37d68848203 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -202,10 +202,8 @@
"%s에 의해 사용 중지되었습니다.""직장 프로필 사용 설정""직장 프로필을 사용 설정할 때까지 개인 앱이 차단됩니다."
-
-
-
-
+ "개인 앱이 %1$s%2$s에 차단됩니다. IT 관리자가 %3$d일 넘게 직장 프로필을 중지하도록 허용하지 않습니다."
+ "사용 설정""나""태블릿 옵션""Android TV 옵션"
@@ -1621,16 +1619,15 @@
"잠금해제 패턴을 %1$d회 잘못 그렸습니다. %2$d회 더 실패하면 이메일 계정을 사용하여 휴대전화를 잠금해제해야 합니다.\n\n %3$d초 후에 다시 시도해 주세요."" — ""삭제"
- "향후 R 빌드에서는 백그라운드에서 시작된 %1$s의 포그라운드 서비스에 더 이상 사용 중인 상태에서 필요한 권한이 부여되지 않습니다. go/r-bg-fgs-restriction 페이지에서 버그 신고를 제출하세요.""권장 수준 이상으로 볼륨을 높이시겠습니까?\n\n높은 볼륨으로 장시간 청취하면 청력에 손상이 올 수 있습니다.""접근성 단축키를 사용하시겠습니까?""단축키가 사용 설정된 경우 볼륨 버튼 두 개를 동시에 3초간 누르면 접근성 기능이 시작됩니다.""접근성 기능을 사용하시겠습니까?"
- "볼륨 키 2개를 몇 초 동안 길게 누르면 접근성 기능이 사용 설정됩니다. 이렇게 되면 기기 작동 방식이 달라질 수 있습니다.\n\n현재 기능:\n%1$s\n설정 > 접근성에서 선택한 기능을 변경할 수 있습니다."
+ "볼륨 키 2개를 몇 초 동안 길게 누르면 접근성 기능이 사용 설정됩니다. 이때 기기 작동 방식이 달라질 수 있습니다.\n\n현재 기능:\n%1$s\n설정 > 접근성에서 선택한 기능을 변경할 수 있습니다."" • %1$s\n""%1$s을(를) 사용하시겠습니까?""볼륨 키 2개를 몇 초 동안 길게 누르면 %1$s 접근성 기능이 사용 설정됩니다. 이렇게 되면 기기 작동 방식이 달라질 수 있습니다.\n\n설정 > 접근성에서 이 단축키를 다른 기능으로 변경할 수 있습니다."
- "사용 설정"
+ "사용""사용 안 함""사용""사용 안함"
@@ -2057,10 +2054,10 @@
"직장""개인 뷰""직장 뷰"
- "직장 앱과 공유할 수 없는 콘텐츠"
- "IT 관리자가 직장 프로필의 앱에서 이 콘텐츠를 공유하도록 허용하지 않습니다."
- "직장 앱으로 열 수 없는 콘텐츠"
- "IT 관리자가 직장 프로필의 앱에서 이 콘텐츠를 열도록 허용하지 않습니다."
+ "직장 앱과 공유할 수 없음"
+ "IT 관리자가 이 콘텐츠를 직장 프로필의 앱과 공유할 수 있도록 허용하지 않았습니다."
+ "직장 앱으로 열 수 없음"
+ "IT 관리자가 이 콘텐츠를 직장 프로필의 앱에서 열 수 있도록 허용하지 않았습니다.""개인 앱과 공유할 수 없는 콘텐츠""IT 관리자가 개인 프로필의 앱에서 이 콘텐츠를 공유하도록 허용하지 않습니다.""개인 앱으로 열 수 없는 콘텐츠"
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index e3cdec901b2699e1b880048ea08f38f90a397aa4..5908475eec048c68f2bdd458d5d7705a0bb2085e 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -202,10 +202,8 @@
"Басып чыгаруу %s тарабынан өчүрүлдү.""Жумуш профилиңизди күйгүзүңүз""Жумуш профилиңизди күйгүзмөйүнчө жеке колдонмолоруңуз бөгөттөлгөн боюнча калат"
-
-
-
-
+ "Жеке колдонмолор %1$s саат %2$s бөгөттөлөт. IT администраторуңуз жумуш профилиңизди %3$d күндөн ашык убакытка өчүрүүгө уруксат бербейт."
+ "Күйгүзүү""Мен""Планшет мүмкүнчүлүктөрү""Android TV параметрлери"
@@ -538,7 +536,7 @@
"Колдонмого сүрөт жыйнагыңызды өзгөртүүгө мүмкүнчүлүк берет.""медиа жыйнагыңыз сакталган жерлерди окуу""Колдонмого медиа жыйнагыңыз сакталган жерлерди окууга мүмкүнчүлүк берет."
- "Сиз экениңизди ырастаңыз"
+ "Өзүңүздү ырастаңыз""Биометрикалык аппарат жеткиликсиз""Аныктыгын текшерүү жокко чыгарылды""Таанылган жок"
@@ -1110,7 +1108,7 @@
"Киргизүү ыкмасы""Текст боюнча иштер""Сактагычта орун калбай баратат"
- "Системанын кээ бир функциялары иштебеши мүмкүн"
+ "Айрым функциялар иштебеши мүмкүн""Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз.""%1$s иштөөдө""Көбүрөөк маалымат үчүн же колдонмону токтотуш үчүн таптап коюңуз."
@@ -1311,7 +1309,7 @@
"Мүчүлүштүктөр USB аркылуу оңдолууда""Өчүрүү үчүн тийип коюңуз""USB аркылуу мүчүлүштүктөрдү оңдоону өчүрүүнү тандаңыз."
- "Мүчүлүштүктөрдү зымсыз оңдоо иштетилди"
+ "Мүчүлүштүктөр Wi-Fi аркылуу оңдолууда""Мүчүлүштүктөрдү зымсыз оңдоону өчүрүү үчүн таптап коюңуз""Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоону өчүрүңүз.""Сыноо программасынын режими иштетилди"
@@ -1549,7 +1547,7 @@
"Жөнөтүлүүдө…""Серепчи иштетилсинби?""Чалуу кабыл алынсынбы?"
- "Дайыма"
+ "Ар дайым""Бир жолу гана""%1$s жумуш профилин колдоого албайт""Планшет"
@@ -1621,17 +1619,16 @@
"Графикалык ачкычты %1$d жолу туура эмес көрсөттүңүз. %2$d жолу туура эмес көрсөтүлгөндөн кийин, телефондун кулпусун ачуу үчүн Google аккаунтуңузга кирүүгө туура келет.\n\n%3$d секундадан кийин кайталап көрсөңүз болот."" — ""Алып салуу"
- "Фондогу %1$s кызматы активдүү режимде иштеп баштап, кийинки R курамаларында колдонуу учурунда уруксаты болбойт. Төмөнкү бөлүмгө өтүп, мүчүлүштүк тууралуу кабарды тапшырыңыз: go/r-bg-fgs-restriction.""Сунушталган деңгээлден да катуулатып уккуңуз келеби?\n\nМузыканы узакка чейин катуу уксаңыз, угууңуз начарлап кетиши мүмкүн.""Ыкчам иштетесизби?""Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз."
- "Атайын мүмкүнчүлүктөр күйгүзүлсүнбү?"
- "Үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып турса, атайын мүмкүнчүлүктөр күйгүзүлөт. Бул түзмөгүңүздүн ишин өзгөртүшү мүмкүн.\n\nУчурдагы функциялар:\n%1$s\nТандалган функцияларды Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнөн өзгөртө аласыз."
+ "Атайын мүмкүнчүлүктөрдү иштетесизби?"
+ "Атайын мүмкүнчүлүктөр функциясын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nУчурдагы функциялар:\n%1$s\nТандалган функцияларды өзгөртүү үчүн, Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."" • %1$s\n""%1$s күйгүзүлсүнбү?"
- "Үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып турса, %1$s, атайын мүмкүнчүлүктөр күйгүзүлөт. Бул түзмөгүңүздүн ишин өзгөртүшү мүмкүн.\n\nБул ыкчам баскычты Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнөн башка функцияга өзгөртө аласыз."
- "Күйгүзүлсүн"
- "Күйгүзүлбөсүн"
+ "%1$s кызматын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nБаскычтардын ушул айкалышын башка функцияга дайындоо үчүн, Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."
+ "Ооба"
+ "Жок""КҮЙҮК""ӨЧҮК""%1$s кызматына түзмөгүңүздү толугу менен көзөмөлдөөгө уруксат бересизби?"
@@ -1645,7 +1642,7 @@
"Жок""Функцияны колдонуп баштоо үчүн аны таптап коюңуз:""Атайын мүмкүнчүлүктөр баскычы менен колдонгуңуз келген функцияларды тандаңыз"
- "Үн деңгээлинин баскычтары менен колдонгуңуз келген функцияларды тандаңыз"
+ "Үндү катуулатуу/акырындатуу баскычтары менен кайсы функцияларды иштеткиңиз келет?""%s өчүрүлдү""Кыска жолдорду түзөтүү""Бүттү"
@@ -1653,8 +1650,8 @@
"Кыска жолду колдонуу""Түстү инверсиялоо""Түсүн тууралоо"
- "Үндү катуулатуу/акырындатуу баскычтарын басып туруңуз. %1$s күйгүзүлдү."
- "Үндү катуулатуу/акырындатуу баскычтарын басып туруңуз. %1$s өчүрүлдү."
+ "Үндү катуулатуу/акырындатуу баскычтары басылып, %1$s күйгүзүлдү."
+ "Үндү катуулатуу/акырындатуу баскычтары басылып, %1$s өчүрүлдү.""%1$s кызматын колдонуу үчүн үнүн чоңойтуп/кичирейтүү баскычтарын үч секунд коё бербей басып туруңуз""Атайын мүмкүнчүлүктөр баскычын таптаганыңызда иштей турган функцияны тандаңыз:""Атайын мүмкүнчүлүктөр жаңсоосу үчүн функцияны тандаңыз (эки манжаңыз менен экрандын ылдый жагынан өйдө карай сүрүңүз):"
@@ -2057,18 +2054,18 @@
"Жумуш""Жеке көрүнүш""Жумуш көрүнүшү"
- "Бул мазмунду жумуш колдонмолору аркылуу бөлүшүүгө болбойт"
- "IT администраторуңуз бул мазмунду жумуш профилиңиздеги колдонмолор аркылуу бөлүшүүгө тыюу салды"
+ "Бул нерсени жумуш колдонмолору аркылуу бөлүшүүгө болбойт"
+ "IT администраторуңуз бул нерсени жумуш профилиңиздеги колдонмолор аркылуу бөлүшүүгө тыюу салды""Жумуш колдонмолору менен ачууга болбойт"
- "IT администраторуңуз бул мазмунду жумуш профилиңиздеги колдонмолор менен ачууга тыюу салды"
+ "IT администраторуңуз бул нерсени жумуш профилиңиздеги колдонмолор менен ачууга тыюу салды""Бул мазмунду жеке колдонмолор аркылуу бөлүшүүгө болбойт"
- "IT администраторуңуз бул мазмунду жеке профилиңиздеги колдонмолор аркылуу бөлүшүүгө тыюу салды"
+ "IT администраторуңуз бул нерсени жеке профилиңиздеги колдонмолор аркылуу бөлүшүүгө тыюу салды""Жеке колдонмолор менен ачууга болбойт"
- "IT администраторуңуз бул мазмунду жеке профилиңиздеги колдонмолор менен ачууга тыюу салды"
+ "IT администраторуңуз бул нерсени жеке профилиңиздеги колдонмолор менен ачууга тыюу салды""Жумуш профили тындырылган""Күйгүзүү"
- "Жумуш колдонмолору бул мазмунду колдоого алышпайт"
- "Жумуш колдонмолору бул мазмунду ача алышпайт"
+ "Бул нерсени колдоого ала турган жумуш колдонмолору жок"
+ "Бул нерсени ача турган жумуш колдонмолору жок""Жеке колдонмолор бул мазмунду колдоого алышпайт""Жеке колдонмолор бул мазмунду ача алышпайт""SIM карта тармагынын кулпусун ачуучу PIN код"
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 521c1b175a224d3371d63a8412c0ced73cd827a9..66ec0e691eed73df513cc72014e9b195064de950 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -202,10 +202,8 @@
"ການພິມຖືກປິດໄວ້ໂດຍ %s.""ເປີດໃຊ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ""ແອັບສ່ວນຕົວຂອງທ່ານຈະຖືກບລັອກໄວ້ຈົນກວ່າທ່ານຈະເປີດໃຊ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານ"
-
-
-
-
+ "ແອັບສ່ວນຕົວຈະຖືກບລັອກໃນວັນທີ %1$s ເວລາ %2$s. ຜູ້ເບິ່ງແຍງລະບົບໄອທີຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ປິດໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານເກີນ %3$d ມື້."
+ "ເປີດໃຊ້""ຂ້າພະເຈົ້າ""ໂຕເລືອກແທັບເລັດ""ຕົວເລືອກ Android TV"
@@ -1313,7 +1311,7 @@
"ເລືອກເພື່ອປິດການດີບັກຜ່ານ USB.""ເຊື່ອມຕໍ່ການດີບັກໄຮ້ສາຍແລ້ວ""ແຕະເພື່ອປິດການດີບັກໄຮ້ສາຍ"
- "ເລືອກປິດການປິດການນຳໃຊ້ການດີບັກໄຮ້ສາຍ."
+ "ເລືອກເພື່ອປິດການນຳໃຊ້ການດີບັກໄຮ້ສາຍ.""ເປີດໃຊ້ໂໝດ Test Harness ແລ້ວ""ດຳເນີນການຣີເຊັດເປັນຄ່າຈາກໂຮງງານເພື່ອປິດການນຳໃຊ້ໂໝດ Test Harness.""ເປີດນຳໃຊ້ຊີຣຽວຄອນໂຊແລ້ວ"
@@ -1621,7 +1619,6 @@
"ທ່ານແຕ້ມຮູບແບບປົດລັອກຂອງທ່ານຜິດ %1$d ເທື່ອແລ້ວ. ຫຼັງຈາກຄວາມພະຍາຍາມອີກ %2$d ເທື່ອ ທ່ານຈະຖືກຖາມໃຫ້ປົດລັອກໂທລະສັບຂອງທ່ານດ້ວຍບັນຊີອີເມວ.\n\n ລອງໃໝ່ອີກຄັ້ງໃນ %3$d ວິນາທີ."" — ""ລຶບອອກ"
- "ບໍລິການພື້ນໜ້າທີ່ເລີ່ມຕົ້ນຈາກພື້ນຫຼັງຈາກ %1$s ຈະບໍ່ມີສິດອະນຸຍາດໃນຂະນະທີ່ໃຊ້ໃນ R builds ໃນອະນາຄົດ. ກະລຸນາອ່ານ go/r-bg-fgs-restriction ແລະ ລາຍງານຂໍ້ຜິດພາດ.""ເພີ່ມລະດັບສຽງໃຫ້ເກີນກວ່າລະດັບທີ່ແນະນຳບໍ?\n\nການຮັບຟັງສຽງໃນລະດັບທີ່ສູງເປັນໄລຍະເວລາດົນອາດເຮັດໃຫ້ການຟັງຂອງທ່ານມີບັນຫາໄດ້.""ໃຊ້ປຸ່ມລັດການຊ່ວຍເຂົ້າເຖິງບໍ?""ເມື່ອເປີດໃຊ້ທາງລັດແລ້ວ, ການກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ 3 ວິນາທີຈະເປັນການເລີ່ມຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ."
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index c9c7b54489fddc06bd9637ed492b52a971caffd9..4f71c361f98a17bf5e82f1733d9a7fec7468b6bf 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -206,10 +206,8 @@
"Neleidžiama spausdinti (%s).""Įjunkite darbo profilį""Asmeninės programos bus užblokuotos, kol įjungsite darbo profilį"
-
-
-
-
+ "Asmeninės programos bus užblokuotos %1$s, %2$s. IT administratorius neleidžia palikti darbo profilio išjungto ilgiau nei %3$d d."
+ "Įjungti""Aš""Planšetinio kompiuterio parinktys""„Android TV“ parinktys"
@@ -1665,7 +1663,6 @@
"Netinkamai nupiešėte atrakinimo piešinį %1$d k. Po dar %2$d nesėkm. band. būsite paprašyti atrakinti telefoną naudodami „Google“ prisijungimo duomenis.\n\n Bandykite dar kartą po %3$d sek."" – ""Pašalinti"
- "Fone pradėtai priekinio plano paslaugai iš „%1$s“ nebus suteiktas leidimas naudojimo metu būsimose R versijose. Apsilankykite go/r-bg-fgs-restriction ir pateikite pranešimą apie riktą.""Padidinti garsą daugiau nei rekomenduojamas lygis?\n\nIlgai klausydami dideliu garsu galite pažeisti klausą.""Naudoti spartųjį pritaikymo neįgaliesiems klavišą?""Kai spartusis klavišas įjungtas, paspaudus abu garsumo mygtukus ir palaikius 3 sekundes bus įjungta pritaikymo neįgaliesiems funkcija."
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index b34538d6a18cf9e3c10078843a623e9afc7af156..f4938cc0a9645d78edc917c53002353b6263f0c0 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -204,10 +204,8 @@
"Drukāšanu atspējoja %s.""Darba profila ieslēgšana""Jūsu personīgās lietotnes būs bloķētas, līdz ieslēgsiet savu darba profilu."
-
-
-
-
+ "Personīgās lietotnes tiks bloķētas šādā datumā: %1$s, plkst. %2$s. Jūsu IT administrators neatļauj atspējot darba profilu ilgāk par %3$d dienām."
+ "Ieslēgt""Man""Planšetdatora opcijas""Android TV opcijas"
@@ -1643,7 +1641,6 @@
"Jūs nepareizi norādījāt atbloķēšanas kombināciju %1$d reizes. Pēc vēl %2$d neveiksmīgiem mēģinājumiem tālrunis būs jāatbloķē, izmantojot e-pasta kontu.\n\nMēģiniet vēlreiz pēc %3$d sekundēm."" — ""Noņemt"
- "Fonā sāktam priekšplāna pakalpojumam no pakotnes %1$s nebūs atļaujas “while-in-use” turpmākajās R versijās. Lūdzu, skatiet vietni go/r-bg-fgs-restriction un iesniedziet kļūdas pārskatu.""Vai palielināt skaļumu virs ieteicamā līmeņa?\n\nIlgstoši klausoties skaņu lielā skaļumā, var tikt bojāta dzirde.""Vai izmantot pieejamības saīsni?""Kad īsinājumtaustiņš ir ieslēgts, nospiežot abas skaļuma pogas un 3 sekundes turot tās, tiks aktivizēta pieejamības funkcija."
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 45e86e1fe92824be74a952234ce87a63d6eb8ff6..f382ee90378ac30cd5223e22573f9b1882b28a37 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -202,10 +202,8 @@
"Печатењето е оневозможено од %s.""Вклучете го работниот профил""Вашите лични апликации се блокирани додека да го вклучите работниот профил"
-
-
-
-
+ "Личните апликации ќе се блокираат на %1$s во %2$s. Вашиот IT администратор не дозволува вашиот работен профил да биде исклучен повеќе од %3$d денови."
+ "Вклучи""Јас""Опции на таблет""Опции на Android TV"
@@ -245,7 +243,7 @@
"Извештај за грешка""Завршете ја сесијата""Слика од екранот"
- "Извештај за грешка"
+ "Извештај за грешки""Ова ќе собира информации за моменталната состојба на вашиот уред, за да ги испрати како порака по е-пошта. Тоа ќе одземе малку време почнувајќи од извештајот за грешки додека не се подготви за праќање; бидете трпеливи.""Интерактивен извештај""Користете го ова во повеќето ситуации. Ви дозволува да го следите напредокот на извештајот, да внесете повеќе детали во врска со проблемот и да сликате слики од екранот. Може да испушти некои помалку користени делови за коишто е потребно долго време за да се пријават."
@@ -1130,10 +1128,10 @@
"Отвори со""Отвори со %1$s""Отвори"
- "Отворајте врски на %1$s со"
- "Отворајте врски со"
- "Отворајте врски со %1$s"
- "Отворајте врски на %1$s со %2$s"
+ "Отворајте линкови на %1$s со"
+ "Отворајте линкови со"
+ "Отворајте линкови со %1$s"
+ "Отворајте линкови на %1$s со %2$s""Дозволи пристап""Измени со""Измени со %1$s"
@@ -1311,7 +1309,7 @@
"Поврзано е отстранување грешки преку USB""Допрете за да го исклучите""Изберете за да се оневозможи отстранување грешки на USB."
- "Безжичното отстранување грешки е поврзано"
+ "Поврзано е безжично отстранување грешки""Допрете за да се исклучи безжичното отстранување грешки""Изберете за да се оневозможи безжично отстранување грешки.""Овозможен е режимот на рамка за тестирање"
@@ -1588,7 +1586,7 @@
"Употреби ја својата шема""Внеси PIN на SIM картичка"
- "Внеси PIN"
+ "Впишете PIN""Внеси лозинка""SIM картичката е сега оневозможена. Внесете ПУК код за да продолжите. Контактирајте го операторот за детали.""Внеси посакуван PIN код"
@@ -1621,7 +1619,6 @@
"Погрешно сте ја употребиле вашата шема на отклучување %1$d пати. По %2$d неуспешни обиди, ќе побараат од вас да го отклучите телефонот со користење сметка на е-пошта.\n\n Обидете се повторно за %3$d секунди."" — ""Отстрани"
- "Услугата од преден план започната во заднина од %1$s нема да има дозола за „додека се користи“ во идните R-верзии. Погледнете на go/r-bg-fgs-restriction и испратете извештај за грешка.""Да го зголемиме звукот над препорачаното ниво?\n\nСлушањето звуци со голема јачина подолги периоди може да ви го оштети сетилото за слух.""Да се користи кратенка за „Пристапност“?""Кога е вклучена кратенката, ако ги притиснете двете копчиња за јачина на звук во времетраење од 3 секунди, ќе се стартува функција за пристапност."
@@ -1761,7 +1758,7 @@
"Услугате %s е инсталирана""Допри да се овозможи""Внесете PIN на админстратор"
- "Внеси PIN"
+ "Впишете PIN""Неточно""Тековен PIN""Нов PIN"
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 4b6ce39f46ab3c488233a4e757dc8357be88c3b1..3ecad6fce138f7a0bc44ba68119b832a566821b5 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -175,7 +175,7 @@
"ഒരുപാട് %s ഇല്ലാതാക്കാൻ ശ്രമിച്ചു.""ടാബ്ലെറ്റ് സ്റ്റോറേജ് കഴിഞ്ഞു. ഇടം ശൂന്യമാക്കാൻ ചില ഫയലുകൾ ഇല്ലാതാക്കുക.""വാച്ചിലെ സ്റ്റോറേജ് നിറഞ്ഞു. ഇടം ശൂന്യമാക്കാൻ കുറച്ച് ഫയലുകൾ ഇല്ലാതാക്കുക."
- "Android ടിവി ഉപകരണ സ്റ്റോറേജ് നിറഞ്ഞിരിക്കുന്നു. ഇടമുണ്ടാക്കാൻ കുറച്ച് ഫയലുകൾ ഇല്ലാതാക്കുക."
+ "Android TV ഉപകരണ സ്റ്റോറേജ് നിറഞ്ഞിരിക്കുന്നു. ഇടമുണ്ടാക്കാൻ കുറച്ച് ഫയലുകൾ ഇല്ലാതാക്കുക.""ഫോൺ സ്റ്റോറേജ് കഴിഞ്ഞു. ഇടം ശൂന്യമാക്കാൻ ചില ഫയലുകൾ ഇല്ലാതാക്കുക."സർട്ടിഫിക്കറ്റ് അതോറിറ്റികൾ ഇൻസ്റ്റാൾ ചെയ്തു
@@ -202,13 +202,11 @@
"%s പ്രിന്റിംഗ് പ്രവർത്തനരഹിതമാക്കി.""ഔദ്യോഗിക പ്രൊഫൈൽ ഓണാക്കുക""നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ ഓണാക്കുന്നത് വരെ നിങ്ങളുടെ വ്യക്തിപരമായ ആപ്പുകൾ ബ്ലോക്കായിരിക്കും"
-
-
-
-
+ "വ്യക്തിപര ആപ്പുകൾ %1$s, %2$s-ന് ബ്ലോക്ക് ചെയ്യപ്പെടും. നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ %3$d ദിവസത്തിൽ കൂടുതൽ ഓഫായ നിലയിൽ തുടരാൻ നിങ്ങളുടെ ഐടി അഡ്മിൻ അനുവദിക്കുന്നില്ല."
+ "ഓണാക്കുക""ഞാന്""ടാബ്ലെറ്റ് ഓപ്ഷനുകൾ"
- "Android ടിവി ഓപ്ഷനുകൾ"
+ "Android TV ഓപ്ഷനുകൾ""ഫോൺ ഓപ്ഷനുകൾ""നിശബ്ദ മോഡ്""വയർലെസ് ഓണാക്കുക"
@@ -226,7 +224,7 @@
"പുനരാരംഭിക്കുന്നു…""ഷട്ട്ഡൗൺ ചെയ്യുന്നു...""നിങ്ങളുടെ ടാബ്ലെറ്റ് ഷട്ട്ഡൗൺ ചെയ്യും."
- "നിങ്ങളുടെ Android ടിവി ഓഫാകും."
+ "നിങ്ങളുടെ Android TV ഓഫാകും.""നിങ്ങളുടെ വാച്ച് ഷട്ട്ഡൗൺ ചെയ്യും.""നിങ്ങളുടെ ഫോൺ ഷട്ട്ഡൗൺ ചെയ്യും.""നിങ്ങൾക്ക് ഷട്ട് ഡൗൺ ചെയ്യണോ?"
@@ -235,7 +233,7 @@
"അടുത്തിടെയുള്ളത്""അടുത്തിടെയുള്ള ആപ്സൊന്നുമില്ല.""ടാബ്ലെറ്റ് ഓപ്ഷനുകൾ"
- "Android ടിവി ഓപ്ഷനുകൾ"
+ "Android TV ഓപ്ഷനുകൾ""ഫോൺ ഓപ്ഷനുകൾ""സ്ക്രീൻ ലോക്ക്""പവർ ഓഫാക്കുക"
@@ -295,7 +293,7 @@
"Android സിസ്റ്റം""വ്യക്തിഗത പ്രൊഫൈലിലേക്ക് മാറുക""ഔദ്യോഗിക പ്രൊഫൈലിലേക്ക് മാറുക"
- "കോൺടാക്റ്റ്"
+ "കോൺടാക്റ്റുകൾ""നിങ്ങളുടെ കോൺടാക്റ്റുകൾ ആക്സസ്സ് ചെയ്യുക""ലൊക്കേഷൻ""ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാൻ"
@@ -359,7 +357,7 @@
"SMS സന്ദേശങ്ങൾ അയയ്ക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് അപ്രതീക്ഷിത നിരക്കുകൾക്കിടയാക്കാം. ക്ഷുദ്രകരമായ അപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ സ്ഥിരീകരണമില്ലാതെ സന്ദേശങ്ങൾ അയയ്ക്കുന്നത് പണച്ചെലവിനിടയാക്കാം.""നിങ്ങളുടെ വാചക സന്ദേശങ്ങൾ വായിക്കുക (SMS അല്ലെങ്കിൽ MMS)""ഈ ആപ്പിന് നിങ്ങളുടെ ടാബ്ലെറ്റിൽ സംഭരിച്ചിരിക്കുന്ന എല്ലാ SMS (വാചക) സന്ദേശങ്ങളും വായിക്കാൻ കഴിയും."
- "നിങ്ങളുടെ Android ടിവിയിൽ സംഭരിച്ചിരിക്കുന്ന എല്ലാ SMS (ടെക്സ്റ്റ്) സന്ദേശങ്ങളും വായിക്കാൻ ഈ ആപ്പിന് കഴിയും."
+ "നിങ്ങളുടെ Android TV-യിൽ സംഭരിച്ചിരിക്കുന്ന എല്ലാ SMS (ടെക്സ്റ്റ്) സന്ദേശങ്ങളും വായിക്കാൻ ഈ ആപ്പിന് കഴിയും.""ഈ ആപ്പിന് നിങ്ങളുടെ ഫോണിൽ സംഭരിച്ചിരിക്കുന്ന എല്ലാ SMS (വാചക) സന്ദേശങ്ങളും വായിക്കാൻ കഴിയും.""വാചക സന്ദേശം നേടുക (WAP)""WAP സന്ദേശങ്ങൾ നേടാനും പ്രോസസ്സുചെയ്യാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. നിങ്ങൾക്ക് അയയ്ക്കുന്ന സന്ദേശങ്ങൾ നിങ്ങൾക്ക് ദൃശ്യമാക്കാതെ തന്നെ നിരീക്ഷിക്കാനോ ഇല്ലാതാക്കാനോ ഉള്ള കഴിവ് ഈ അനുമതികളിൽ ഉൾപ്പെടുന്നു."
@@ -381,7 +379,7 @@
"ഈ ആപ്പിന് പശ്ചാത്തലത്തിൽ ഡാറ്റ ഉപയോഗിക്കാൻ കഴിയും. ഇത് ഡാറ്റ ഉപയോഗം വർദ്ധിപ്പിച്ചേക്കാം.""അപ്ലിക്കേഷൻ എപ്പോഴും പ്രവർത്തിക്കുന്നതാക്കുക""മെമ്മറിയിൽ അപ്ലിക്കേഷനുകളുടെ ഭാഗങ്ങൾ നിലനിർത്താൻ സ്വയം അനുവദിക്കുന്നു. ഇത് ടാബ്ലെറ്റിനെ മന്ദഗതിയിലാക്കുന്ന വിധത്തിൽ മറ്റ് അപ്ലിക്കേഷനുകൾക്ക് ലഭ്യമായ മെമ്മറി പരിമിതപ്പെടുത്താനിടയുണ്ട്."
- "ആപ്പിന്റെ ഭാഗങ്ങളെ മെമ്മറിയിൽ സ്ഥിരമായി നിലനിർത്താൻ അതിനെ അനുവദിക്കുന്നു. ഇത് മറ്റ് ആപ്പുകൾക്ക് ലഭ്യമായ മെമ്മറി പരിമിതപ്പെടുത്തുകയും Android ടിവിയുടെ വേഗത കുറയ്ക്കുകയും ചെയ്യും."
+ "ആപ്പിന്റെ ഭാഗങ്ങളെ മെമ്മറിയിൽ സ്ഥിരമായി നിലനിർത്താൻ അതിനെ അനുവദിക്കുന്നു. ഇത് മറ്റ് ആപ്പുകൾക്ക് ലഭ്യമായ മെമ്മറി പരിമിതപ്പെടുത്തുകയും Android TV-യുടെ വേഗത കുറയ്ക്കുകയും ചെയ്യും.""മെമ്മറിയിൽ അപ്ലിക്കേഷനുകളുടെ ഭാഗങ്ങൾ നിലനിർത്താൻ സ്വയം അനുവദിക്കുന്നു. ഇത് ഫോണിനെ മന്ദഗതിയിലാക്കുന്ന വിധത്തിൽ മറ്റ് അപ്ലിക്കേഷനുകൾക്ക് ലഭ്യമായ മെമ്മറി പരിമിതപ്പെടുത്താനിടയുണ്ട്.""മുൻവശത്തുള്ള സേവനം റൺ ചെയ്യുക""മുൻവശത്തുള്ള സേവനങ്ങൾ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."
@@ -391,35 +389,35 @@
"സിസ്റ്റത്തിന്റെ സുരക്ഷ ക്രമീകരണങ്ങളുടെ ഡാറ്റ പരിഷ്ക്കരിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ദോഷകരമായ അപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ സിസ്റ്റത്തിന്റെ കോൺഫിഗറേഷനെ കേടാക്കിയേക്കാം.""സ്റ്റാർട്ടപ്പിൽ പ്രവർത്തിക്കുക""സിസ്റ്റം ബൂട്ടുചെയ്യുന്നത് പൂർത്തിയാകുമ്പോൾ തന്നെ സ്വയം ആരംഭിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് ടാബ്ലെറ്റ് അരംഭിക്കുന്നതിന് കൂടുതൽ ദൈർഘ്യമെടുക്കുന്നതിന് കാരണമാകാം ഒപ്പം പ്രവർത്തിക്കുമ്പോഴെല്ലാം ടാബ്ലെറ്റിന്റെ മൊത്തത്തിലുള്ള വേഗത കുറയ്ക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കും."
- "സിസ്റ്റം ബൂട്ട് ചെയ്യൽ പൂർത്തിയായിക്കഴിഞ്ഞ് ഉടൻ സ്വയം ആരംഭിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. ഇതിന് നിങ്ങളുടെ Android ടിവി ആരംഭിക്കുന്നതിന്റെ വേഗത കുറയ്ക്കാനും എപ്പോഴും പ്രവർത്തിക്കാൻ ആപ്പിനെ അനുവദിച്ചുകൊണ്ട് മൊത്തത്തിൽ ഉപകരണത്തിന്റെ വേഗത കുറയ്ക്കാനും കഴിയും."
+ "സിസ്റ്റം ബൂട്ട് ചെയ്യൽ പൂർത്തിയായിക്കഴിഞ്ഞ് ഉടൻ സ്വയം ആരംഭിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. ഇതിന് നിങ്ങളുടെ Android TV ആരംഭിക്കുന്നതിന്റെ വേഗത കുറയ്ക്കാനും എപ്പോഴും പ്രവർത്തിക്കാൻ ആപ്പിനെ അനുവദിച്ചുകൊണ്ട് മൊത്തത്തിൽ ഉപകരണത്തിന്റെ വേഗത കുറയ്ക്കാനും കഴിയും.""സിസ്റ്റം ബൂട്ടുചെയ്യുന്നത് പൂർത്തിയാകുമ്പോൾ തന്നെ സ്വയം ആരംഭിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് ഫോൺ ആരംഭിക്കുന്നതിന് കൂടുതൽ ദൈർഘ്യമെടുക്കാം ഒപ്പം പ്രവർത്തിക്കുമ്പോഴെല്ലാം മൊത്തം ഫോണിന്റെ മൊത്തത്തിലുള്ള വേഗത കുറയ്ക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കും.""സ്റ്റിക്കി പ്രക്ഷേപണം അയയ്ക്കുക""സ്റ്റിക്കി പ്രക്ഷേപണങ്ങൾ അയയ്ക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു, പ്രക്ഷേപണം അവസാനിച്ചതിനുശേഷവും അത് നിലനിൽക്കുന്നു. അമിതോപയോഗം വളരെയധികം മെമ്മറി ഉപയോഗിക്കുന്നതിനാൽ, അത് ടാബ്ലെറ്റിന്റെ പ്രവർത്തനത്തെ മന്ദഗതിയിലാക്കുകയോ അസ്ഥിരമാക്കുകയോ ചെയ്യാം."
- "പ്രക്ഷേപണം അവസാനിച്ച ശേഷവും അവശേഷിക്കുന്ന സ്റ്റിക്കി പ്രക്ഷേപണങ്ങൾ അയയ്ക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. Android ടിവിയുടെ അമിതമായ ഉപയോഗം ഒരുപാട് മെമ്മറി ഉപയോഗിക്കാൻ കാരണമാകുകയും ടിവിയുടെ വേഗത കുറയ്ക്കുകയോ അതിനെ അസ്ഥിരമാക്കുകയോ ചെയ്തേക്കാം."
+ "പ്രക്ഷേപണം അവസാനിച്ച ശേഷവും അവശേഷിക്കുന്ന സ്റ്റിക്കി പ്രക്ഷേപണങ്ങൾ അയയ്ക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. Android TV-യുടെ അമിതമായ ഉപയോഗം ഒരുപാട് മെമ്മറി ഉപയോഗിക്കാൻ കാരണമാകുകയും ടിവിയുടെ വേഗത കുറയ്ക്കുകയോ അതിനെ അസ്ഥിരമാക്കുകയോ ചെയ്തേക്കാം.""സ്റ്റിക്കി പ്രക്ഷേപണങ്ങൾ അയയ്ക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു, പ്രക്ഷേപണം അവസാനിച്ചതിനുശേഷവും അത് നിലനിൽക്കുന്നു. അമിതോപയോഗം വളരെയധികം മെമ്മറി ഉപയോഗിക്കുന്നതിനാൽ, അത് ഫോണിന്റെ പ്രവർത്തനത്തെ മന്ദഗതിയിലാക്കുകയോ അസ്ഥിരമാക്കുകയോ ചെയ്യാം.""നിങ്ങളുടെ കോൺടാക്റ്റുകൾ റീഡുചെയ്യുക""ടാബ്ലെറ്റിൽ സംഭരിച്ച നിങ്ങളുടെ കോൺടാക്റ്റുകളെക്കുറിച്ചുള്ള ഡാറ്റ വായിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ ടാബ്ലെറ്റിൽ കോണ്ടാക്റ്റുകൾ സൃഷ്ടിച്ച അക്കൗണ്ടുകളിലേക്കുള്ള ആക്സസും ആപ്പുകൾക്ക് ഉണ്ടായിരിക്കും. നിങ്ങൾ ഇൻസ്റ്റാൾ ചെയ്ത ആപ്പുകൾ സൃഷ്ടിച്ച അക്കൗണ്ടുകളും ഇതിൽ ഉൾപ്പെട്ടേക്കാം. നിങ്ങളുടെ കോണ്ടാക്റ്റ് ഡാറ്റ സംരക്ഷിക്കാൻ ആപ്പുകളെ ഈ അനുമതി അനുവദിക്കുന്നു, നിങ്ങളുടെ അറിവില്ലാതെ, ദോഷകരമായ ആപ്പുകൾ കോണ്ടാക്റ്റ് ഡാറ്റ പങ്കിടുകയും ചെയ്തേക്കാം."
- "നിങ്ങളുടെ Android ടിവിയിൽ സംഭരിച്ച കോൺടാക്റ്റുകളെക്കുറിച്ചുള്ള ഡാറ്റ വായിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ Android ടിവിയിൽ കോണ്ടാക്റ്റുകൾ സൃഷ്ടിച്ച അക്കൗണ്ടുകളിലേക്കുള്ള ആക്സസും ആപ്പുകൾക്ക് ഉണ്ടായിരിക്കും. നിങ്ങൾ ഇൻസ്റ്റാൾ ചെയ്ത ആപ്പുകൾ സൃഷ്ടിച്ച അക്കൗണ്ടുകളും ഇതിൽ ഉൾപ്പെട്ടേക്കാം. നിങ്ങളുടെ കോണ്ടാക്റ്റ് ഡാറ്റ സംരക്ഷിക്കാൻ ആപ്പുകളെ ഈ അനുമതി അനുവദിക്കുന്നു, നിങ്ങളുടെ അറിവില്ലാതെ, ദോഷകരമായ ആപ്പുകൾ കോണ്ടാക്റ്റ് ഡാറ്റ പങ്കിടുകയും ചെയ്തേക്കാം."
+ "നിങ്ങളുടെ Android TV-യിൽ സംഭരിച്ച കോൺടാക്റ്റുകളെക്കുറിച്ചുള്ള ഡാറ്റ വായിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ Android TV-യിൽ കോണ്ടാക്റ്റുകൾ സൃഷ്ടിച്ച അക്കൗണ്ടുകളിലേക്കുള്ള ആക്സസും ആപ്പുകൾക്ക് ഉണ്ടായിരിക്കും. നിങ്ങൾ ഇൻസ്റ്റാൾ ചെയ്ത ആപ്പുകൾ സൃഷ്ടിച്ച അക്കൗണ്ടുകളും ഇതിൽ ഉൾപ്പെട്ടേക്കാം. നിങ്ങളുടെ കോണ്ടാക്റ്റ് ഡാറ്റ സംരക്ഷിക്കാൻ ആപ്പുകളെ ഈ അനുമതി അനുവദിക്കുന്നു, നിങ്ങളുടെ അറിവില്ലാതെ, ദോഷകരമായ ആപ്പുകൾ കോണ്ടാക്റ്റ് ഡാറ്റ പങ്കിടുകയും ചെയ്തേക്കാം.""ഉപകരണത്തിൽ സംഭരിച്ച നിങ്ങളുടെ കോൺടാക്റ്റുകളെക്കുറിച്ചുള്ള ഡാറ്റ വായിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ ഫോണിൽ കോണ്ടാക്റ്റുകൾ സൃഷ്ടിച്ച അക്കൗണ്ടുകളിലേക്കുള്ള ആക്സസും ആപ്പുകൾക്ക് ഉണ്ടായിരിക്കും. നിങ്ങൾ ഇൻസ്റ്റാൾ ചെയ്ത ആപ്പുകൾ സൃഷ്ടിച്ച അക്കൗണ്ടുകളും ഇതിൽ ഉൾപ്പെട്ടേക്കാം. നിങ്ങളുടെ കോണ്ടാക്റ്റ് ഡാറ്റ സംരക്ഷിക്കാൻ ആപ്പുകളെ ഈ അനുമതി അനുവദിക്കുന്നു, നിങ്ങളുടെ അറിവില്ലാതെ, ദോഷകരമായ ആപ്പുകൾ കോണ്ടാക്റ്റ് ഡാറ്റ പങ്കിടുകയും ചെയ്തേക്കാം.""നിങ്ങളുടെ കോൺടാക്റ്റുകൾ പരിഷ്ക്കരിക്കുക""ടാബ്ലെറ്റിൽ സംഭരിച്ച നിങ്ങളുടെ കോൺടാക്റ്റുകളെക്കുറിച്ചുള്ള ഡാറ്റ പരിഷ്ക്കരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. കോൺടാക്റ്റ് ഡാറ്റ ഇല്ലാതാക്കാൻ അപ്പുകളെ ഈ അനുമതി അനുവദിക്കുന്നു."
- "നിങ്ങളുടെ Android ടിവിയിൽ സംഭരിച്ച കോൺടാക്റ്റുകളെക്കുറിച്ചുള്ള ഡാറ്റ പരിഷ്ക്കരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. കോൺടാക്റ്റ് ഡാറ്റ ഇല്ലാതാക്കാൻ അപ്പുകളെ ഈ അനുമതി അനുവദിക്കുന്നു."
+ "നിങ്ങളുടെ Android TV-യിൽ സംഭരിച്ച കോൺടാക്റ്റുകളെക്കുറിച്ചുള്ള ഡാറ്റ പരിഷ്ക്കരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. കോൺടാക്റ്റ് ഡാറ്റ ഇല്ലാതാക്കാൻ അപ്പുകളെ ഈ അനുമതി അനുവദിക്കുന്നു.""ഫോണിൽ സംഭരിച്ച നിങ്ങളുടെ കോൺടാക്റ്റുകളെക്കുറിച്ചുള്ള ഡാറ്റ പരിഷ്ക്കരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. കോൺടാക്റ്റ് ഡാറ്റ ഇല്ലാതാക്കാൻ അപ്പുകളെ ഈ അനുമതി അനുവദിക്കുന്നു.""കോൾ ചരിത്രം റീഡ് ചെയ്യുക""ഈ ആപ്പിന് നിങ്ങളുടെ കോൾ ചരിത്രം വായിക്കാൻ കഴിയും.""കോൾ ചരിത്രം റൈറ്റ് ചെയ്യുക""ഇൻകമ്മിംഗ്-ഔട്ട്ഗോയിംഗ് കോളുകളെക്കുറിച്ചുള്ള ഡാറ്റയുൾപ്പെടെയുള്ള നിങ്ങളുടെ ടാബ്ലെറ്റിന്റെ കോൾ ചരിത്രം പരിഷ്ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.ഇതു വഴി കോൾ ചരിത്ര ഡാറ്റകൾ പരിഷ്ക്കരിക്കാനും ഇല്ലാതാക്കാനും ദോഷകരമായ അപ്ലിക്കേഷനുകൾക്ക് കഴിഞ്ഞേയ്ക്കാം."
- "ഇൻകമിംഗ്, ഔട്ട്ഗോയിംഗ് കോളുകളെക്കുറിച്ചുള്ള ഡാറ്റ ഉൾപ്പെടെ നിങ്ങളുടെ Android ടിവിയിലെ കോൾ ചരിത്രം പരിഷ്ക്കരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ കോൾ ചരിത്രം മായ്ക്കാനോ പരിഷ്ക്കരിക്കാനോ ദോഷകരമായ ആപ്പുകൾ ഇത് ഉപയോഗിച്ചേക്കാം."
+ "ഇൻകമിംഗ്, ഔട്ട്ഗോയിംഗ് കോളുകളെക്കുറിച്ചുള്ള ഡാറ്റ ഉൾപ്പെടെ നിങ്ങളുടെ Android TV-യിലെ കോൾ ചരിത്രം പരിഷ്ക്കരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ കോൾ ചരിത്രം മായ്ക്കാനോ പരിഷ്ക്കരിക്കാനോ ദോഷകരമായ ആപ്പുകൾ ഇത് ഉപയോഗിച്ചേക്കാം.""ഇൻകമ്മിംഗ്-ഔട്ട്ഗോയിംഗ് കോളുകളെക്കുറിച്ചുള്ള ഡാറ്റയുൾപ്പെടെയുള്ള നിങ്ങളുടെ ഫോണിന്റെ കോൾ ചരിത്രം പരിഷ്ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.ഇതു വഴി കോൾ ചരിത്ര ഡാറ്റകൾ പരിഷ്ക്കരിക്കാനും ഇല്ലാതാക്കാനും ദോഷകരമായ അപ്ലിക്കേഷനുകൾക്ക് കഴിഞ്ഞേയ്ക്കാം.""ശരീര സെൻസറുകൾ (ഹൃദയമിടിപ്പ് നിരക്ക് മോണിറ്ററുകൾ പോലെ) ആക്സസ് ചെയ്യുക""നിങ്ങളുടെ ഹൃദയമിടിപ്പ് പോലുള്ള ശാരീരികാവസ്ഥ നിരീക്ഷിക്കാൻ സെൻസറുകളിൽ നിന്ന് വിവരം ആക്സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.""കലണ്ടർ ഇവന്റുകളും വിശദാംശങ്ങളും വായിക്കുക""ഈ ആപ്പിന് നിങ്ങളുടെ ടാബ്ലെറ്റിൽ സംഭരിച്ചിരിക്കുന്ന എല്ലാ കലണ്ടർ ഇവന്റുകളും വായിക്കാനും നിങ്ങളുടെ കലണ്ടർ വിവരങ്ങൾ പങ്കിടാനും അല്ലെങ്കിൽ സംരക്ഷിക്കാനും കഴിയും."
- "ഈ ആപ്പിന് നിങ്ങളുടെ Android ടിവിയിൽ സംഭരിച്ചിരിക്കുന്ന എല്ലാ കലണ്ടർ ഇവന്റുകളും വായിക്കാനും നിങ്ങളുടെ കലണ്ടർ ഡാറ്റ പങ്കിടാനോ സംരക്ഷിക്കാനോ സാധിക്കുകയും ചെയ്യും."
+ "ഈ ആപ്പിന് നിങ്ങളുടെ Android TV-യിൽ സംഭരിച്ചിരിക്കുന്ന എല്ലാ കലണ്ടർ ഇവന്റുകളും വായിക്കാനും നിങ്ങളുടെ കലണ്ടർ ഡാറ്റ പങ്കിടാനോ സംരക്ഷിക്കാനോ സാധിക്കുകയും ചെയ്യും.""ഈ ആപ്പിന് നിങ്ങളുടെ ഫോണിൽ സംഭരിച്ചിരിക്കുന്ന എല്ലാ കലണ്ടർ ഇവന്റുകളും വായിക്കാനും നിങ്ങളുടെ കലണ്ടർ വിവരങ്ങൾ പങ്കിടാനും അല്ലെങ്കിൽ സംരക്ഷിക്കാനും കഴിയും.""ഉടമകളുടെ അറിവില്ലാതെ കലണ്ടർ ഇവന്റുകൾ ചേർക്കുകയോ പരിഷ്ക്കരിക്കുകയോ ചെയ്ത് അതിഥികൾക്ക് ഇമെയിൽ അയയ്ക്കുക""ഈ ആപ്പിന്, നിങ്ങളുടെ ടാബ്ലെറ്റിൽ കലണ്ടർ ഇവന്റുകൾ ചേർക്കാനോ നീക്കംചെയ്യാനോ മാറ്റാനോ കഴിയും. കലണ്ടർ ഉടമകളിൽ നിന്നാണ് വരുന്നതെന്ന് തോന്നിപ്പിക്കാവുന്ന സന്ദേശങ്ങൾ അയയ്ക്കാനോ ഉടമകളെ അറിയിക്കാതെ അവരുടെ ഇവന്റുകളെ മാറ്റാനോ ഈ ആപ്പിന് കഴിയും."
- "നിങ്ങളുടെ Android ടിവിയിൽ കലണ്ടർ ഇവന്റുകൾ ചേർക്കാനും നീക്കം ചെയ്യാനും മാറ്റാനും ഈ ആപ്പിന് കഴിയും. കലണ്ടർ ഉടമകളിൽ നിന്ന് വരുന്നതെന്ന് തോന്നിപ്പിക്കുന്ന സന്ദേശങ്ങൾ അയയ്ക്കാനോ ഉടമകൾക്ക് അറിയിപ്പ് നൽകാതെ ഇവന്റുകൾ മാറ്റാനോ ഈ ആപ്പിന് കഴിയും."
+ "നിങ്ങളുടെ Android TV-യിൽ കലണ്ടർ ഇവന്റുകൾ ചേർക്കാനും നീക്കം ചെയ്യാനും മാറ്റാനും ഈ ആപ്പിന് കഴിയും. കലണ്ടർ ഉടമകളിൽ നിന്ന് വരുന്നതെന്ന് തോന്നിപ്പിക്കുന്ന സന്ദേശങ്ങൾ അയയ്ക്കാനോ ഉടമകൾക്ക് അറിയിപ്പ് നൽകാതെ ഇവന്റുകൾ മാറ്റാനോ ഈ ആപ്പിന് കഴിയും.""ഈ ആപ്പിന്, നിങ്ങളുടെ ഫോണിൽ കലണ്ടർ ഇവന്റുകൾ ചേർക്കാനോ നീക്കംചെയ്യാനോ മാറ്റാനോ കഴിയും. കലണ്ടർ ഉടമകളിൽ നിന്നാണ് വരുന്നതെന്ന് തോന്നിപ്പിക്കാവുന്ന സന്ദേശങ്ങൾ അയയ്ക്കാനോ ഉടമകളെ അറിയിക്കാതെ അവരുടെ ഇവന്റുകളെ മാറ്റാനോ ഈ ആപ്പിന് കഴിയും.""ലൊക്കേഷൻ ദാതാവിന്റെ അധിക കമാൻഡുകൾ ആക്സസ്സുചെയ്യുക""ലൊക്കേഷൻ ദാതാവിന്റെ അധിക കമാൻഡുകൾ ആക്സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് GPS-ന്റെയോ മറ്റ് ലൊക്കേഷൻ ഉറവിടങ്ങളുടെയോ പ്രവർത്തനത്തിൽ ഇടപെടാൻ അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്."
@@ -464,15 +462,15 @@
"ഉപകരണത്തിന്റെ ഫോൺ നമ്പറുകൾ ആക്സസ് ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു.""കാറിലെ സ്ക്രീൻ ഓണാക്കി വയ്ക്കുക""ഉറങ്ങുന്നതിൽ നിന്ന് ടാബ്ലെറ്റിനെ തടയുക"
- "നിങ്ങളുടെ Android ടിവി ഉറങ്ങുന്നതിൽ നിന്ന് തടയുക"
+ "നിങ്ങളുടെ Android TV ഉറങ്ങുന്നതിൽ നിന്ന് തടയുക""ഉറങ്ങുന്നതിൽ നിന്ന് ഫോണിനെ തടയുക""കാറിലെ സ്ക്രീൻ ഓണാക്കി വയ്ക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു.""ടാബ്ലെറ്റ് സുഷുപ്തിയിലാകുന്നതിൽ നിന്നും തടയുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."
- "നിങ്ങളുടെ Android ടിവിയെ ഉറങ്ങുന്നതിൽ നിന്ന് തടയാൻ ആപ്പിനെ അനുവദിക്കുന്നു."
+ "നിങ്ങളുടെ Android TV-യെ ഉറങ്ങുന്നതിൽ നിന്ന് തടയാൻ ആപ്പിനെ അനുവദിക്കുന്നു.""ഫോൺ സുഷുപ്തിയിലാകുന്നതിൽ നിന്നും തടയുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.""ഇൻഫ്രാറെഡ് അയയ്ക്കുക""ടാബ്ലെറ്റിന്റെ ഇൻഫ്രാറെഡ് ട്രാൻസ്മിറ്റർ ഉപയോഗിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."
- "നിങ്ങളുടെ Android ടിവിയുടെ ഇൻഫ്രാറെഡ് ട്രാൻസ്മിറ്റർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."
+ "നിങ്ങളുടെ Android TV-യുടെ ഇൻഫ്രാറെഡ് ട്രാൻസ്മിറ്റർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു.""ഫോണിന്റെ ഇൻഫ്രാറെഡ് ട്രാൻസ്മിറ്റർ ഉപയോഗിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.""വാൾപേപ്പർ സജ്ജീകരിക്കുക""സിസ്റ്റം വാൾപേപ്പറിനെ സജ്ജീകരിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."
@@ -480,11 +478,11 @@
"സിസ്റ്റം വലുപ്പ സൂചനകളെ സജ്ജീകരിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.""സമയ മേഖല സജ്ജീകരിക്കുക""ടാബ്ലെറ്റിന്റെ സമയ മേഖലയെ മാറ്റുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."
- "നിങ്ങളുടെ Android ടിവിയുടെ സമയമേഖല മാറ്റാൻ ആപ്പിനെ അനുവദിക്കുന്നു."
+ "നിങ്ങളുടെ Android TV-യുടെ സമയമേഖല മാറ്റാൻ ആപ്പിനെ അനുവദിക്കുന്നു.""ഫോണിന്റെ സമയ മേഖലയെ മാറ്റുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.""ഉപകരണത്തിലെ അക്കൗണ്ടുകൾ കണ്ടെത്തുക""ടാബ്ലെറ്റ് തിരിച്ചറിയുന്ന അക്കൗണ്ടുകളുടെ ലിസ്റ്റ് നേടാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇതിൽ നിങ്ങൾ ഇൻസ്റ്റാളുചെയ്ത അപ്ലിക്കേഷനുകൾ സൃഷ്ടിച്ച എല്ലാ അക്കൗണ്ടുകളും ഉൾപ്പെടാം."
- "നിങ്ങളുടെ Android ടിവി തിരിച്ചറിയുന്ന അക്കൗണ്ടുകളുടെ ലിസ്റ്റ് നേടാൻ ആപ്പിനെ അനുവദിക്കുന്നു. നിങ്ങൾ ഇൻസ്റ്റാൾ ചെയ്ത ആപ്പുകൾ സൃഷ്ടിച്ച ഏത് അക്കൗണ്ടുകളും ഇതിൽ ഉൾപ്പെടാം."
+ "നിങ്ങളുടെ Android TV തിരിച്ചറിയുന്ന അക്കൗണ്ടുകളുടെ ലിസ്റ്റ് നേടാൻ ആപ്പിനെ അനുവദിക്കുന്നു. നിങ്ങൾ ഇൻസ്റ്റാൾ ചെയ്ത ആപ്പുകൾ സൃഷ്ടിച്ച ഏത് അക്കൗണ്ടുകളും ഇതിൽ ഉൾപ്പെടാം.""ഫോൺ തിരിച്ചറിയുന്ന അക്കൗണ്ടുകളുടെ ലിസ്റ്റ് നേടാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇതിൽ നിങ്ങൾ ഇൻസ്റ്റാളുചെയ്ത അപ്ലിക്കേഷനുകൾ സൃഷ്ടിച്ച എല്ലാ അക്കൗണ്ടുകളും ഉൾപ്പെടാം.""നെറ്റ്വർക്ക് കണക്ഷനുകൾ കാണുക""ഏതെല്ലാം നെറ്റ്വർക്കുകൾ നിലവിലുണ്ടെന്നതും കണക്റ്റുചെയ്തിട്ടുണ്ടെന്നതും പോലുള്ള നെറ്റ്വർക്ക് കണക്ഷനുകളെക്കുറിച്ചുള്ള വിവരം കാണാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."
@@ -500,21 +498,21 @@
"വൈഫൈ ആക്സസ്സ് പോയിന്റുകളിലേക്ക് കണക്റ്റുചെയ്യാനും അതിൽ നിന്ന് വിച്ഛേദിക്കാനും വൈഫൈ നെറ്റ്വർക്കുകൾക്കായി ഉപകരണ കോൺഫിഗറേഷൻ മാറ്റാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.""വൈഫൈ മൾട്ടികാസ്റ്റ് റിസപ്ഷൻ അനുവദിക്കുക""മൾട്ടികാസ്റ്റ് വിലാസങ്ങൾ ഉപയോഗിച്ച് നിങ്ങളുടെ ടബ്ലെറ്റിലേക്ക് മാത്രമല്ലാതെ, ഒരു വൈഫൈ നെറ്റ്വർക്കിലെ എല്ലാ ഉപകരണങ്ങളിലേക്കും അയച്ച പായ്ക്കറ്റുകൾ നേടാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് മൾട്ടികാസ്റ്റ് ഇതര മോഡിനേക്കാൾ അധികം പവർ ഉപയോഗിക്കുന്നു."
- "മൾട്ടികാസ്റ്റ് വിലാസങ്ങൾ ഉപയോഗിച്ച് നിങ്ങളുടെ Android ടിവിയിലേക്ക് മാത്രമല്ലാതെ ഒരു വൈഫൈ നെറ്റ്വർക്കിലെ എല്ലാ ഉപകരണങ്ങളിലേക്കും അയച്ച പാക്കറ്റുകൾ സ്വീകരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. ഇത് മൾട്ടികാസ്റ്റ് ഇതര മോഡിനേക്കാൾ കൂടുതൽ പവർ ഉപയോഗിക്കുന്നു."
+ "മൾട്ടികാസ്റ്റ് വിലാസങ്ങൾ ഉപയോഗിച്ച് നിങ്ങളുടെ Android TV-യിലേക്ക് മാത്രമല്ലാതെ ഒരു വൈഫൈ നെറ്റ്വർക്കിലെ എല്ലാ ഉപകരണങ്ങളിലേക്കും അയച്ച പാക്കറ്റുകൾ സ്വീകരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. ഇത് മൾട്ടികാസ്റ്റ് ഇതര മോഡിനേക്കാൾ കൂടുതൽ പവർ ഉപയോഗിക്കുന്നു.""മൾട്ടികാസ്റ്റ് വിലാസങ്ങൾ ഉപയോഗിച്ച് നിങ്ങളുടെ ഫോണിലേക്ക് മാത്രമല്ലാതെ, ഒരു വൈഫൈ നെറ്റ്വർക്കിലെ എല്ലാ ഉപകരണങ്ങളിലേക്കും അയച്ച പായ്ക്കറ്റുകൾ നേടാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് മൾട്ടികാസ്റ്റ് ഇതര മോഡിനേക്കാൾ അധികം പവർ ഉപയോഗിക്കുന്നു.""ബ്ലൂടൂത്ത് ക്രമീകരണങ്ങൾ ആക്സസ്സുചെയ്യുക""ഒരു പ്രാദേശിക ബ്ലൂടൂത്ത് ടാബ്ലെറ്റ് കോൺഫിഗർചെയ്യുന്നതിനും വിദൂര ഉപകരണങ്ങളെ കണ്ടെത്തി ജോടിയാക്കുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."
- "നിങ്ങളുടെ Android ടിവിയിൽ Bluetooth കോൺഫിഗർ ചെയ്യാനും വിദൂര ഉപകരണങ്ങൾ കണ്ടെത്താനും അവ ജോടിയാക്കാനും ആപ്പിനെ അനുവദിക്കുന്നു."
+ "നിങ്ങളുടെ Android TV-യിൽ Bluetooth കോൺഫിഗർ ചെയ്യാനും വിദൂര ഉപകരണങ്ങൾ കണ്ടെത്താനും അവ ജോടിയാക്കാനും ആപ്പിനെ അനുവദിക്കുന്നു.""ഒരു പ്രാദേശിക ബ്ലൂടൂത്ത് ഫോണിനെ കോൺഫിഗർചെയ്യുന്നതിനും വിദൂര ഉപകരണങ്ങളെ കണ്ടെത്തി ജോടിയാക്കുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.""WiMAX കണക്റ്റുചെയ്യുക, അതിൽ നിന്നും വിച്ഛേദിക്കുക""WiMAX പ്രവർത്തനക്ഷമമാണോയെന്നതും കണക്റ്റുചെയ്തിരിക്കുന്ന ഏതെങ്കിലും WiMAX നെറ്റ്വർക്കുകളെക്കുറിച്ചുള്ള വിവരങ്ങളും നിർണ്ണയിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.""WiMAX നില മാറ്റുക""WiMAX നെറ്റ്വർക്കുകളിലേക്ക് ടാബ്ലെറ്റ് കണക്റ്റുചെയ്യാനും അതിൽ നിന്ന് വിച്ഛേദിക്കാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."
- "WiMAX നെറ്റ്വർക്കുകളിലേക്ക് നിങ്ങളുടെ Android ടിവി കണക്റ്റ് ചെയ്യാനും അതിൽ നിന്ന് Android ടിവിയെ വിച്ഛേദിക്കാനും ആപ്പിനെ അനുവദിക്കുന്നു."
+ "WiMAX നെറ്റ്വർക്കുകളിലേക്ക് നിങ്ങളുടെ Android TV കണക്റ്റ് ചെയ്യാനും അതിൽ നിന്ന് Android TV-യെ വിച്ഛേദിക്കാനും ആപ്പിനെ അനുവദിക്കുന്നു.""WiMAX നെറ്റ്വർക്കുകളിലേക്ക് ഫോൺ കണക്റ്റുചെയ്യാനും അതിൽ നിന്ന് വിച്ഛേദിക്കാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.""ബ്ലൂടൂത്ത് ഉപകരണങ്ങളുമായി ജോടിയാക്കുക""ടാബ്ലെറ്റിലെ ബ്ലൂടൂത്ത് കോൺഫിഗറേഷൻ കാണാനും ജോടിയാക്കിയ ഉപകരണങ്ങളുമായി കണക്ഷനുകൾ നടത്തി അംഗീകരിക്കാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."
- "നിങ്ങളുടെ Android ടിവിയിലെ Bluetooth കോൺഫിഗറേഷൻ കാണാനും ജോടിയാക്കിയ ഉപകരണങ്ങളുമായി കണക്ഷനുകൾ സൃഷ്ടിക്കാനും അംഗീകരിക്കാനും ആപ്പിനെ അനുവദിക്കുന്നു."
+ "നിങ്ങളുടെ Android TV-യിലെ Bluetooth കോൺഫിഗറേഷൻ കാണാനും ജോടിയാക്കിയ ഉപകരണങ്ങളുമായി കണക്ഷനുകൾ സൃഷ്ടിക്കാനും അംഗീകരിക്കാനും ആപ്പിനെ അനുവദിക്കുന്നു.""ഫോണിലെ ബ്ലൂടൂത്ത് കോൺഫിഗറേഷൻ കാണാനും ജോടിയാക്കിയ ഉപകരണങ്ങളുമായി കണക്ഷനുകൾ നടത്തി അംഗീകരിക്കാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു.""തിരഞ്ഞെടുത്ത NFC പേയ്മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ""റൂട്ട് ലക്ഷ്യസ്ഥാനം, രജിസ്റ്റർ ചെയ്തിരിക്കുന്ന സഹായങ്ങൾ എന്നിവ പോലുള്ള, തിരഞ്ഞെടുത്ത NFC പേയ്മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ ലഭിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."
@@ -675,10 +673,10 @@
"സ്ക്രീൻ ലോക്ക് പാസ്വേഡുകളിലും PIN-കളിലും അനുവദിച്ചിരിക്കുന്ന ദൈർഘ്യവും പ്രതീകങ്ങളും നിയന്ത്രിക്കുക.""സ്ക്രീൻ അൺലോക്ക് ശ്രമങ്ങൾ നിരീക്ഷിക്കുക""സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക, വളരെയധികം തെറ്റായ പാസ്വ്ഡുകൾ ടൈപ്പുചെയ്തിട്ടുണ്ടെങ്കിൽ ടാബ്ലെറ്റ് ലോക്കുചെയ്യുകയോ ടാബ്ലെറ്റിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോ ചെയ്യുക."
- "സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്തിട്ടുണ്ടെങ്കിൽ നിങ്ങളുടെ Android ടിവി ലോക്ക് ചെയ്യുകയോ Android ടിവിയിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോ ചെയ്യുക."
+ "സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്തിട്ടുണ്ടെങ്കിൽ നിങ്ങളുടെ Android TV ലോക്ക് ചെയ്യുകയോ Android TV-യിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോ ചെയ്യുക.""സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുക, വളരെയധികം തെറ്റായ പാസ്വ്ഡുകൾ ടൈപ്പുചെയ്തിട്ടുണ്ടെങ്കിൽ ഫോൺ ലോക്കുചെയ്യുകയോ ഫോണിലെ എല്ലാ ഡാറ്റയും മായ്ക്കുകയോചെയ്യുക.""സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ പാസ്വേഡ് ടൈപ്പുചെയ്തെങ്കിൽ ടാബ്ലെറ്റ് ലോക്കുചെയ്യുകയോ ഈ എല്ലാ ഉപയോക്തൃവിവരവും മായ്ക്കുകയോ ചെയ്യുക."
- "സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്തിട്ടുണ്ടെങ്കിൽ നിങ്ങളുടെ Android ടിവി ലോക്ക് ചെയ്യുകയോ ഈ ഉപയോക്തൃ ഡാറ്റയെല്ലാം മായ്ക്കുകയോ ചെയ്യുക."
+ "സ്ക്രീൻ അൺലോക്ക് ചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പ് ചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ തെറ്റായ പാസ്വേഡുകൾ ടൈപ്പ് ചെയ്തിട്ടുണ്ടെങ്കിൽ നിങ്ങളുടെ Android TV ലോക്ക് ചെയ്യുകയോ ഈ ഉപയോക്തൃ ഡാറ്റയെല്ലാം മായ്ക്കുകയോ ചെയ്യുക.""സ്ക്രീൻ അൺലോക്കുചെയ്യുമ്പോൾ തെറ്റായി ടൈപ്പുചെയ്ത പാസ്വേഡുകളുടെ എണ്ണം നിരീക്ഷിക്കുകയും നിരവധി തവണ പാസ്വേഡ് ടൈപ്പുചെയ്തെങ്കിൽ ഫോൺ ലോക്കുചെയ്യുകയോ ഈ എല്ലാ ഉപയോക്തൃവിവരവും മായ്ക്കുകയോ ചെയ്യുക.""സ്ക്രീൻ ലോക്ക് മാറ്റുക""സ്ക്രീൻ ലോക്ക് മാറ്റുക."
@@ -686,11 +684,11 @@
"സ്ക്രീൻ ലോക്കുകൾ എങ്ങനെ വേണമെന്നും എപ്പോൾ വേണമെന്നും എന്നത് നിയന്ത്രിക്കുക""എല്ലാ ഡാറ്റയും മായ്ക്കുക""ഒരു ഫാക്ടറി ഡാറ്റ പുനഃസജ്ജീകരണം നടപ്പിലാക്കുന്നതിലൂടെ ടാബ്ലെറ്റിന്റെ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."
- "ഫാക്ടറി ഡാറ്റ റീസെറ്റ് ചെയ്ത് നിങ്ങളുടെ Android ടിവിയിലെ ഉപകരണ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."
+ "ഫാക്ടറി ഡാറ്റ റീസെറ്റ് ചെയ്ത് നിങ്ങളുടെ Android TV-യിലെ ഉപകരണ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക.""ഒരു ഫാക്ടറി ഡാറ്റ പുനഃസജ്ജീകരണം നടപ്പിലാക്കുന്നതിലൂടെ ഫോണിന്റെ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക.""ഉപയോക്തൃ ഡാറ്റ മായ്ക്കുക""മുന്നറിയിപ്പൊന്നും നൽകാതെ ഈ ടാബ്ലെറ്റിലെ ഈ ഉപയോക്താവിന്റെ ഡാറ്റ മായ്ക്കുക."
- "ഈ Android ടിവിയിലെ ഈ ഉപയോക്തൃ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക."
+ "ഈ Android TV-യിലെ ഈ ഉപയോക്തൃ ഡാറ്റ മുന്നറിയിപ്പില്ലാതെ മായ്ക്കുക.""മുന്നറിയിപ്പൊന്നും നൽകാതെ ഈ ഫോണിലെ ഈ ഉപയോക്താവിന്റെ ഡാറ്റ മായ്ക്കുക.""ഉപകരണ ഗ്ലോബൽ പ്രോക്സി സജ്ജീകരിക്കുക""നയം പ്രവർത്തനക്ഷമമാക്കിയിരിക്കുമ്പോൾ ഉപകരണ ഗ്ലോബൽ പ്രോക്സി ഉപയോഗിക്കുന്നത് സജ്ജമാക്കുക. ഉപകരണ ഉടമയ്ക്ക് മാത്രമേ ഗ്ലോബൽ പ്രോക്സി സജ്ജമാക്കാനാകൂ."
@@ -840,7 +838,7 @@
"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ശ്രമങ്ങളുടെ പരമാവധി കഴിഞ്ഞു""സിം കാർഡില്ല""ടാബ്ലെറ്റിൽ സിം കാർഡൊന്നുമില്ല."
- "നിങ്ങളുടെ Android ടിവിയിൽ സിം കാർഡില്ല."
+ "നിങ്ങളുടെ Android TV-യിൽ സിം കാർഡില്ല.""ഫോണിൽ സിം കാർഡൊന്നുമില്ല.""ഒരു സിം കാർഡ് ചേർക്കുക.""സിം കാർഡ് കാണുന്നില്ല അല്ലെങ്കിൽ റീഡുചെയ്യാനായില്ല. ഒരു സിം കാർഡ് ചേർക്കുക."
@@ -863,13 +861,13 @@
"നിങ്ങളുടെ പാസ്വേഡ് %1$d തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n%2$d സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക.""നിങ്ങളുടെ പിൻ %1$d തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n%2$d സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക.""നിങ്ങളുടെ അൺലോക്കുചെയ്യൽ പാറ്റേൺ %1$d തവണ തെറ്റായി വരച്ചു. %2$d തെറ്റായ ശ്രമങ്ങൾക്കുശേഷം, Google സൈൻ ഇൻ ചെയ്യൽ ഉപയോഗിച്ച് നിങ്ങളുടെ ടാബ്ലെറ്റ് അൺലോക്കുചെയ്യുന്നതിന് ആവശ്യപ്പടും.\n\n %3$d സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക."
- "നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ %1$d തവണ തെറ്റായി വരച്ചു. %2$d ശ്രമങ്ങൾ കൂടി പരാജയപ്പെട്ടാൽ Google സൈൻ ഇൻ ഉപയോഗിച്ച് നിങ്ങളുടെ Android ടിവി അൺലോക്ക് ചെയ്യാൻ ആവശ്യപ്പെടും.\n\n %3$d സെക്കൻഡിന് ശേഷം വീണ്ടും ശ്രമിക്കുക."
+ "നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ %1$d തവണ തെറ്റായി വരച്ചു. %2$d ശ്രമങ്ങൾ കൂടി പരാജയപ്പെട്ടാൽ Google സൈൻ ഇൻ ഉപയോഗിച്ച് നിങ്ങളുടെ Android TV അൺലോക്ക് ചെയ്യാൻ ആവശ്യപ്പെടും.\n\n %3$d സെക്കൻഡിന് ശേഷം വീണ്ടും ശ്രമിക്കുക.""നിങ്ങൾ അൺലോക്കുചെയ്യൽ പാറ്റേൺ %1$d തവണ തെറ്റായി വരച്ചു. %2$d തെറ്റായ ശ്രമങ്ങൾക്കുശേഷം, Google സൈൻ ഇൻ ചെയ്യൽ ഉപയോഗിച്ച് നിങ്ങളുടെ ഫോൺ അൺലോക്കുചെയ്യുന്നതിന് ആവശ്യപ്പടും. \n\n %3$d സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക.""നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ %1$d തെറ്റായി ശ്രമിച്ചു. %2$d ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ടാബ്ലെറ്റ് ഫാക്ടറി സ്ഥിരമായതിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും."
- "നിങ്ങൾ %1$d തവണ തെറ്റായി Android ടിവി അൺലോക്ക് ചെയ്യാൻ ശ്രമിച്ചു. %2$d ശ്രമങ്ങൾ കൂടി പരാജയപ്പെട്ടാൽ, നിങ്ങളുടെ Android ടിവി ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് റീസെറ്റ് ചെയ്യപ്പെടുകയും എല്ലാ ഉപയോക്തൃ ഡാറ്റയും നഷ്ടപ്പെടുകയും ചെയ്യും."
+ "നിങ്ങൾ %1$d തവണ തെറ്റായി Android TV അൺലോക്ക് ചെയ്യാൻ ശ്രമിച്ചു. %2$d ശ്രമങ്ങൾ കൂടി പരാജയപ്പെട്ടാൽ, നിങ്ങളുടെ Android TV ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് റീസെറ്റ് ചെയ്യപ്പെടുകയും എല്ലാ ഉപയോക്തൃ ഡാറ്റയും നഷ്ടപ്പെടുകയും ചെയ്യും.""നിങ്ങൾ %1$d തവണ ഫോൺ അൺലോക്കുചെയ്യാൻ തെറ്റായി ശ്രമിച്ചു. %2$d ശ്രമങ്ങൾ കൂടി പരാജയപ്പെടുകയാണെങ്കിൽ, ഫോൺ ഫാക്ടറി സ്ഥിരമായതിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും.""നിങ്ങൾ %d തവണ ടാബ്ലെറ്റ് അൺലോക്കുചെയ്യാൻ തെറ്റായി ശ്രമിച്ചു. ടാബ്ലെറ്റ് ഇപ്പോൾ ഫാക്ടറി സ്ഥിരമായതിലേക്ക് പുനസജ്ജീകരിക്കും."
- "നിങ്ങൾ %d തവണ തെറ്റായി Android ടിവി അൺലോക്ക് ചെയ്യാൻ ശ്രമിച്ചു. നിങ്ങളുടെ Android ടിവി ഇപ്പോൾ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് റീസെറ്റ് ചെയ്യപ്പെടും."
+ "നിങ്ങൾ %d തവണ തെറ്റായി Android TV അൺലോക്ക് ചെയ്യാൻ ശ്രമിച്ചു. നിങ്ങളുടെ Android TV ഇപ്പോൾ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് റീസെറ്റ് ചെയ്യപ്പെടും.""നിങ്ങൾ %d തവണ ഫോൺ അൺലോക്കുചെയ്യാൻ തെറ്റായി ശ്രമിച്ചു. ഫോൺ ഇപ്പോൾ ഫാക്ടറി സ്ഥിരമായതിലേക്ക് പുനസജ്ജീകരിക്കും.""%d നിമിഷത്തിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക.""പാറ്റേൺ മറന്നോ?"
@@ -956,7 +954,7 @@
"ബ്രൗസർ സന്ദർശിച്ച എല്ലാ URL-കളുടെയും ചരിത്രവും ബ്രൗസറിന്റെ എല്ലാ ബുക്ക്മാർക്കുകളും റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ശ്രദ്ധിക്കുക: ഈ അനുമതി മൂന്നാം കക്ഷി ബ്രൗസറുകളോ വെബ് ബ്രൗസിംഗ് കഴിവുകളുള്ള മറ്റ് അപ്ലിക്കേഷനുകളോ നടപ്പിലാക്കാനിടയില്ല.""വെബ് ബുക്ക്മാർക്കുകളും ചരിത്രവും റൈറ്റുചെയ്യുക""നിങ്ങളുടെ ടാബ്ലെറ്റിൽ സംഭരിച്ചിരിക്കുന്ന ബ്രൗസറിന്റെ ചരിത്രമോ ബുക്ക്മാർക്കുകളോ പരിഷ്ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് ബ്രൗസർ ഡാറ്റ മായ്ക്കാനോ പരിഷ്ക്കരിക്കാനോ അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്. ശ്രദ്ധിക്കുക: ഈ അനുമതി മൂന്നാം കക്ഷി ബ്രൗസറുകളോ വെബ് ബ്രൗസിംഗ് കഴിവുകളുള്ള മറ്റ് അപ്ലിക്കേഷനുകളോ നടപ്പിലാക്കാനിടയില്ല."
- "നിങ്ങളുടെ Android ടിവിയിൽ സംഭരിച്ചിരിക്കുന്ന ബ്രൗസറിന്റെ ചരിത്രവും ബുക്ക്മാർക്കുകളും പരിഷ്ക്കരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. ബ്രൗസർ ഡാറ്റ മായ്ക്കാനോ പരിഷ്ക്കരിക്കാനോ ഇത് ആപ്പിനെ അനുവദിച്ചേക്കാം. ശ്രദ്ധിക്കുക: മൂന്നാം കക്ഷി ബ്രൗസറുകൾക്കോ വെബ് ബ്രൗസിംഗ് ശേഷികളുള്ള മറ്റ് ആപ്പുകൾക്കോ ഈ അനുമതി നടപ്പിലാക്കാനായേക്കില്ല."
+ "നിങ്ങളുടെ Android TV-യിൽ സംഭരിച്ചിരിക്കുന്ന ബ്രൗസറിന്റെ ചരിത്രവും ബുക്ക്മാർക്കുകളും പരിഷ്ക്കരിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. ബ്രൗസർ ഡാറ്റ മായ്ക്കാനോ പരിഷ്ക്കരിക്കാനോ ഇത് ആപ്പിനെ അനുവദിച്ചേക്കാം. ശ്രദ്ധിക്കുക: മൂന്നാം കക്ഷി ബ്രൗസറുകൾക്കോ വെബ് ബ്രൗസിംഗ് ശേഷികളുള്ള മറ്റ് ആപ്പുകൾക്കോ ഈ അനുമതി നടപ്പിലാക്കാനായേക്കില്ല.""നിങ്ങളുടെ ഫോണിൽ സംഭരിച്ചിരിക്കുന്ന ബ്രൗസറിന്റെ ചരിത്രമോ ബുക്ക്മാർക്കുകളോ പരിഷ്ക്കരിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് ബ്രൗസർ ഡാറ്റ മായ്ക്കാനോ പരിഷ്ക്കരിക്കാനോ അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്. ശ്രദ്ധിക്കുക: ഈ അനുമതി മൂന്നാം കക്ഷി ബ്രൗസറുകളോ വെബ് ബ്രൗസിംഗ് കഴിവുകളുള്ള മറ്റ് അപ്ലിക്കേഷനുകളോ നടപ്പിലാക്കാനിടയില്ല.""ഒരു അലാറം സജ്ജീകരിക്കുക""ഒരു ഇൻസ്റ്റാളുചെയ്ത അലാറം ക്ലോക്ക് അപ്ലിക്കേഷനിൽ അലാറം സജ്ജീകരിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ചില അലാറം ക്ലോക്ക് അപ്ലിക്കേഷനുകൾ ഈ സവിശേഷത നടപ്പിലാക്കാതിരുന്നേക്കാം."
@@ -1311,7 +1309,7 @@
"USB ഡീബഗ്ഗിംഗ് കണക്റ്റ് ചെയ്തു""USB ഡീബഗ്ഗിംഗ് ഓഫാക്കാൻ ടാപ്പ് ചെയ്യുക""USB ഡീബഗ്ഗുചെയ്യൽ പ്രവർത്തനരഹിതമാക്കാൻ തിരഞ്ഞെടുക്കുക."
- "വയർലെസ് ഡീബഗ്ഗിംഗ് കണക്റ്റ് ചെയ്തിരിക്കുന്നു"
+ "വയർലെസ് ഡീബഗ്ഗിംഗ് കണക്റ്റ് ചെയ്തു""വയർലെസ് ഡീബഗ്ഗിംഗ് ഓഫാക്കാൻ ടാപ്പ് ചെയ്യുക""വയർലെസ് ഡീബഗ്ഗിംഗ് പ്രവർത്തനരഹിതമാക്കാൻ തിരഞ്ഞെടുക്കുക.""പരിശോധനാ സംവിധാനങ്ങൾ മോഡ് പ്രവർത്തനക്ഷമമാക്കി"
@@ -1549,7 +1547,7 @@
"അയയ്ക്കുന്നു…""ബ്രൗസർ സമാരംഭിക്കണോ?""കോൾ സ്വീകരിക്കണോ?"
- "എല്ലായ്പ്പോഴും"
+ "എല്ലായ്പ്പോഴും""ഒരിക്കൽ മാത്രം""%1$s, ഔദ്യോഗിക പ്രൊഫൈലിനെ പിന്തുണയ്ക്കുന്നില്ല""ടാബ്ലെറ്റ്"
@@ -1611,22 +1609,21 @@
"നിങ്ങളുടെ പാസ്വേഡ് %1$d തവണ തെറ്റായി ടൈപ്പുചെയ്തു. \n\n%2$d സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക.""നിങ്ങളുടെ പാറ്റേൺ %1$d തവണ തെറ്റായി വരച്ചു. \n\n%2$d സെക്കൻഡിനുള്ളിൽ വീണ്ടും ശ്രമിക്കുക.""നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ %1$d തെറ്റായി ശ്രമിച്ചു. %2$d ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ടാബ്ലെറ്റ് ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും."
- "നിങ്ങൾ %1$d തവണ തെറ്റായി Android ടിവി അൺലോക്ക് ചെയ്യാൻ ശ്രമിച്ചു. %2$d ശ്രമങ്ങൾ കൂടി പരാജയപ്പെട്ടാൽ, നിങ്ങളുടെ Android ടിവി ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് റീസെറ്റ് ചെയ്യപ്പെടുകയും എല്ലാ ഉപയോക്തൃ ഡാറ്റയും നഷ്ടപ്പെടുകയും ചെയ്യും."
+ "നിങ്ങൾ %1$d തവണ തെറ്റായി Android TV അൺലോക്ക് ചെയ്യാൻ ശ്രമിച്ചു. %2$d ശ്രമങ്ങൾ കൂടി പരാജയപ്പെട്ടാൽ, നിങ്ങളുടെ Android TV ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് റീസെറ്റ് ചെയ്യപ്പെടുകയും എല്ലാ ഉപയോക്തൃ ഡാറ്റയും നഷ്ടപ്പെടുകയും ചെയ്യും.""നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ %1$d തെറ്റായി ശ്രമിച്ചു. %2$d ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ഫോൺ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് പുനഃസജ്ജികരിക്കുകയും ഉപയോക്തൃ ഡാറ്റയെല്ലാം നഷ്ടപ്പെടുകയും ചെയ്യും.""നിങ്ങൾ ടാബ്ലെറ്റ് അൺലോക്കുചെയ്യാൻ തവണ %d തെറ്റായി ശ്രമിച്ചു. ടാബ്ലെറ്റ് ഇപ്പോൾ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് പുനസജ്ജീകരിക്കും."
- "നിങ്ങൾ %d തവണ തെറ്റായി Android ടിവി അൺലോക്ക് ചെയ്യാൻ ശ്രമിച്ചു. നിങ്ങളുടെ Android ടിവി ഇപ്പോൾ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് റീസെറ്റ് ചെയ്യപ്പെടും."
+ "നിങ്ങൾ %d തവണ തെറ്റായി Android TV അൺലോക്ക് ചെയ്യാൻ ശ്രമിച്ചു. നിങ്ങളുടെ Android TV ഇപ്പോൾ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് റീസെറ്റ് ചെയ്യപ്പെടും.""നിങ്ങൾ ഫോൺ അൺലോക്കുചെയ്യാൻ തവണ %d തെറ്റായി ശ്രമിച്ചു. ഫോൺ ഇപ്പോൾ ഫാക്ടറി ഡിഫോൾട്ടിലേക്ക് പുനസജ്ജീകരിക്കും.""നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ %1$d തവണ തെറ്റായി വരച്ചു. %2$d ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ഒരു ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് ടാബ്ലെറ്റ് അൺലോക്ക് ചെയ്യാൻ നിങ്ങളോട് ആവശ്യപ്പെടും.\n\n %3$d സെക്കൻഡിനുള്ള വീണ്ടും ശ്രമിക്കുക."
- "നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ %1$d തവണ തെറ്റായി വരച്ചു. %2$d ശ്രമങ്ങൾ കൂടി പരാജയപ്പെട്ടാൽ ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് നിങ്ങളുടെ Android ടിവി അൺലോക്ക് ചെയ്യാൻ ആവശ്യപ്പെടും.\n\n %3$d സെക്കൻഡിന് ശേഷം വീണ്ടും ശ്രമിക്കുക."
+ "നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ %1$d തവണ തെറ്റായി വരച്ചു. %2$d ശ്രമങ്ങൾ കൂടി പരാജയപ്പെട്ടാൽ ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് നിങ്ങളുടെ Android TV അൺലോക്ക് ചെയ്യാൻ ആവശ്യപ്പെടും.\n\n %3$d സെക്കൻഡിന് ശേഷം വീണ്ടും ശ്രമിക്കുക.""നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ %1$d തവണ തെറ്റായി വരച്ചു. %2$d ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ഒരു ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് ഫോൺ അൺലോക്ക് ചെയ്യാൻ നിങ്ങളോട് ആവശ്യപ്പെടും.\n\n %3$d സെക്കൻഡിനുള്ള വീണ്ടും ശ്രമിക്കുക."" — ""നീക്കംചെയ്യുക"
- "%1$s എന്നതിൽ നിന്നുള്ള പശ്ചാത്തലത്തിൽ ആരംഭിച്ച് ഫോർഗ്രൗണ്ടിൽ വരുന്ന സേവനത്തിന് ഭാവി R ബിൽഡുകളിൽ, \'ഉപയോഗിക്കുമ്പോൾ മാത്രമുള്ള അനുമതി\' ഉണ്ടായിരിക്കില്ല. go/r-bg-fgs-restriction കണ്ട് ബഗ് റിപ്പോർട്ട് ഫയൽ ചെയ്യുക.""മുകളിൽക്കൊടുത്തിരിക്കുന്ന ശുപാർശചെയ്ത ലെവലിലേക്ക് വോളിയം വർദ്ധിപ്പിക്കണോ?\n\nഉയർന്ന വോളിയത്തിൽ ദീർഘനേരം കേൾക്കുന്നത് നിങ്ങളുടെ ശ്രവണ ശേഷിയെ ദോഷകരമായി ബാധിക്കാം.""ഉപയോഗസഹായി കുറുക്കുവഴി ഉപയോഗിക്കണോ?""കുറുക്കുവഴി ഓണായിരിക്കുമ്പോൾ, രണ്ട് വോളിയം ബട്ടണുകളും 3 സെക്കൻഡ് നേരത്തേക്ക് അമർത്തുന്നത് ഉപയോഗസഹായി ഫീച്ചർ ആരംഭിക്കും.""ഉപയോഗസഹായി ഫീച്ചറുകൾ ഓണാക്കണോ?"
- "രണ്ട് വോളിയം കീകളും അൽപ്പ നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുന്നത്, ഉപയോഗസഹായി ഫീച്ചറുകൾ ഓണാക്കുന്നു. നിങ്ങളുടെ ഉപകരണം പ്രവർത്തിക്കുന്ന വിധം ഇത് മാറ്റിയേക്കാം.\n\nനിലവിലുള്ള ഫീച്ചറുകൾ:\n%1$s\nതിരഞ്ഞെടുത്ത ഫീച്ചറുകൾ ക്രമീകരണം > ഉപയോഗസഹായി എന്നതിൽ മാറ്റാനാവും."
+ "രണ്ട് വോളിയം കീകളും അൽപ്പ നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുന്നത്, ഉപയോഗസഹായി ഫീച്ചറുകൾ ഓണാക്കുന്നു. നിങ്ങളുടെ ഉപകരണം പ്രവർത്തിക്കുന്ന രീതിയെ ഇത് മാറ്റിയേക്കാം.\n\nനിലവിലുള്ള ഫീച്ചറുകൾ:\n%1$s\nതിരഞ്ഞെടുത്ത ഫീച്ചറുകൾ ക്രമീകരണം > ഉപയോഗസഹായി എന്നതിൽ മാറ്റാനാവും."" • %1$s\n""%1$s ഓണാക്കണോ?""രണ്ട് വോളിയം കീകളും അൽപ്പ നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുന്നത് ഉപയോഗസഹായി ഫീച്ചറായ %1$s എന്നതിനെ ഓണാക്കുന്നു. നിങ്ങളുടെ ഉപകരണം പ്രവർത്തിക്കുന്ന വിധം ഇത് മാറ്റിയേക്കാം.\n\nക്രമീകരണം > ഉപയോഗസഹായി എന്നതിലെ മറ്റൊരു ഫീച്ചറിലേക്ക് നിങ്ങൾക്ക് ഈ കുറുക്കുവഴി മാറ്റാനാവും."
@@ -1643,9 +1640,9 @@
"ഇതിന് ഒരു ആപ്പുമായോ ഹാർഡ്വെയർ സെൻസറുമായോ ഉള്ള നിങ്ങളുടെ ആശയവിനിമയങ്ങൾ ട്രാക്ക് ചെയ്യാനും നിങ്ങളുടെ പേരിൽ ആശയവിനിമയം നടത്താനും കഴിയും.""അനുവദിക്കൂ""നിരസിക്കുക"
- "ഇത് ഉപയോഗിക്കാൻ ആരംഭിക്കുന്നതിന് ഫീച്ചർ ടാപ്പ് ചെയ്യുക:"
+ "ഉപയോഗിച്ച് തുടങ്ങാൻ ഫീച്ചർ ടാപ്പ് ചെയ്യുക:""ഉപയോഗസഹായി ബട്ടണിന്റെ സഹായത്തോടെ, ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക"
- "വോളിയം കീ കുറുക്കുവഴിയുടെ സഹായത്തോടെ, ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക"
+ "വോളിയം കീ കുറുക്കുവഴിയിലൂടെ ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക""%s ഓഫാക്കിയിരിക്കുന്നു""കുറുക്കുവഴികൾ തിരുത്തുക""പൂർത്തിയാക്കി"
@@ -1654,7 +1651,7 @@
"വർണ്ണ വിപര്യയം""നിറം ക്രമീകരിക്കൽ""വോളിയം കീകൾ പിടിച്ചു. %1$s ഓണാക്കി."
- "വോളിയം കീകൾ പിടിച്ചു. %1$s ഓഫാക്കി."
+ "വോളിയം കീകൾ അമർത്തിപ്പിടിച്ചു. %1$s ഓഫാക്കി.""%1$s ഉപയോഗിക്കാൻ, രണ്ട് വോളിയം കീകളും മൂന്ന് സെക്കൻഡ് അമർത്തിപ്പിടിക്കുക""നിങ്ങൾ ഉപയോഗസഹായി ബട്ടൺ ടാപ്പ് ചെയ്യുമ്പോൾ ഉപയോഗിക്കുന്നതിന് ഒരു ഫീച്ചർ തിരഞ്ഞെടുക്കുക:""ഉപയോഗസഹായി വിരൽചലനത്തോടൊപ്പം ഉപയോഗിക്കാൻ ഒരു ഫീച്ചർ തിരഞ്ഞെടുക്കുക (രണ്ട് വിരലുകളുപയോഗിച്ച് സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലോട്ട് സ്വൈപ്പ് ചെയ്യുക):"
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 40a57be658040ec1ed48223de689e0dbe864c2bc..121b71c1b4a7421b4c02a92a2b4e4fd2cb4d2bca 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -175,7 +175,7 @@
"Хэт олон %s-г устгах оролдлого хийсэн байна.""Таблетийн сан дүүрсэн. Зай чөлөөлөх бол зарим файлыг устгана уу.""Цагны сан дүүрсэн. Зай чөлөөлөх бол зарим файлыг устгана уу."
- "Android TВ төхөөрөмжийн хадгалах сан дүүрсэн байна. Зай гаргахын тулд зарим файлыг устгана уу."
+ "Android TV төхөөрөмжийн хадгалах сан дүүрсэн байна. Зай гаргахын тулд зарим файлыг устгана уу.""Утасны сан дүүрсэн. Зай чөлөөлөх бол зарим файлыг устгана уу."Сертификатын эрхийг суулгасан
@@ -202,13 +202,11 @@
"%s хэвлэх үйлдлийг идэвхгүй болгосон.""Ажлын профайлаа асаах""Та ажлын профайлыг асаах хүртэл таны хувийн аппуудыг хориглосон"
-
-
-
-
+ "Хувийн аппуудыг %1$s-н %2$s-д блоклоно. Таны IT админ таны ажлын профайлыг %3$d-с олон хоног унтраалттай байхыг зөвшөөрдөггүй."
+ "Асаах""Би""Таблетын сонголтууд"
- "Android ТВ-н сонголт"
+ "Android TV-н сонголт""Утасны сонголт""Дуугүй горим""Утасгүй холбоог асаах"
@@ -226,7 +224,7 @@
"Дахин эхэлж байна...""Унтрааж байна…""Таны таблет унтрах болно."
- "Таны Android ТВ төхөөрөмж унтарна."
+ "Таны Android TV төхөөрөмж унтарна.""Таны цаг унтрах болно.""Таны утас унтрах болно.""Та унтраах уу?"
@@ -235,7 +233,7 @@
"Сүүлийн""Сүүлийн апп хоосон.""Таблет сонголт"
- "Android ТВ-н сонголт"
+ "Android TV-н сонголт""Утасны сонголтууд""Дэлгэцний түгжээ""Унтраах"
@@ -359,7 +357,7 @@
"Апп нь SMS мессеж илгээх боломжтой. Энэ нь санаандгүй төлбөрт оруулж болзошгүй. Хортой апп нь таны зөвшөөрөлгүйгээр мессеж илгээн таныг төлбөрт оруулж болзошгүй.""таны текст мессежийг унших(SMS эсвэл MMS)""Энэ апп таны таблетад хадгалсан бүх SMS (текст) зурвасыг унших боломжтой."
- "Энэ апп таны Android ТВ төхөөрөмжид хадгалсан бүх SMS (текст) мессежийг уншиж чадна."
+ "Энэ апп таны Android TV төхөөрөмжид хадгалсан бүх SMS (текст) мессежийг уншиж чадна.""Энэ апп таны утсанд хадгалсан бүх SMS (текст) зурвасыг унших боломжтой.""текст мессеж(WAP) хүлээн авах""Апп нь WAP мессежийг хүлээн авах болон биелүүлэх боломжтой. Энэ зөвшөөрөл нь танд илгээсэн мессежийг танд харуулалгүйгээр хянах эсвэл устгах боломжийг агуулна."
@@ -381,7 +379,7 @@
"Энэ апп цаана ажиллах боломжтой. Энэ нь датаны хэрэглээг нэмэгдүүлж болзошгүй.""апп-г байнга ажиллуулах""Апп нь өөрийн хэсгийн санах ойд байнга байлгах боломжтой. Энэ нь бусад апп-уудын ашиглах санах ойг хязгаарлан таблетыг удаашруулах болно."
- "Аппад өөрийн хэсгийг санах ойд тогтмол хадгалахыг зөвшөөрнө. Энэ нь таны Android ТВ төхөөрөмжийг удаашруулж буй бусад аппад боломжтой санах ойг хязгаарлаж болно."
+ "Аппад өөрийн хэсгийг санах ойд тогтмол хадгалахыг зөвшөөрнө. Энэ нь таны Android TV төхөөрөмжийг удаашруулж буй бусад аппад боломжтой санах ойг хязгаарлаж болно.""Апп нь өөрийн хэсгийг санах ойд байнга байлгах боломжтой. Энэ нь бусад апп-уудын ашиглах санах ойг хязгаарлан утсыг удаашруулах болно.""интерактив (foreground) үйлчилгээг ажиллуулах""Аппад интерактив (foreground) үйлчилгээг ашиглахыг зөвшөөрнө үү."
@@ -391,35 +389,35 @@
"Апп нь системийн тохиргооны датаг өөрчлөх боломжтой. Хортой апп нь таны системийн тохиргоог сүйтгэх боломжтой.""Эхлэхэд ажиллуулах""Апп нь систем асаж дуусахад шууд өөрийгөө асаах боломжтой. Ингэснээр таблетыг асахад их хугацаа орох болон байнга ажилладаг апп нь таблетийг бүхэлд нь удаашруулах боломжтой."
- "Аппад системийг ачаалж дуусмагц өөрийгөө эхлүүлэхийг зөвшөөрнө. Энэ нь таны Android ТВ төхөөрөмжийн эхлэх хугацааг удаашруулах боломжтойгоос гадна аппыг тогтмол ажиллуулснаар төхөөрөмжийн ерөнхий хурдыг удаашруулж болзошгүй."
+ "Аппад системийг ачаалж дуусмагц өөрийгөө эхлүүлэхийг зөвшөөрнө. Энэ нь таны Android TV төхөөрөмжийн эхлэх хугацааг удаашруулах боломжтойгоос гадна аппыг тогтмол ажиллуулснаар төхөөрөмжийн ерөнхий хурдыг удаашруулж болзошгүй.""Апп нь систем асаж дуусахад шууд өөрийгөө асаах боломжтой. Ингэснээр утсыг асахад их хугацаа орох болон байнга ажилладаг апп нь утсыг бүхэлд нь удаашруулах боломжтой.""тасардаггүй өргөн дамжууллыг илгээх""Апп нь өргөн дамжуулал дууссаны дараа үлдсэн өргөн дамжуулалыг илгээх боломжтой. Ихээр ашиглах нь хэт их санах ой ашиглан таблетыг удаашруулах болон тогтворгүй болгох боломжтой."
- "Аппад нэвтрүүлэг дууссаны дараа үлдэх бэхлэгдсэн нэвтрүүлэг илгээхийг зөвшөөрнө. Хэт их ашиглах нь санах ойн ачааллыг нэмэгдүүлж, улмаар таны Android ТВ төхөөрөмжийг удаан эсвэл тогтворгүй болгож болзошгүй."
+ "Аппад нэвтрүүлэг дууссаны дараа үлдэх бэхлэгдсэн нэвтрүүлэг илгээхийг зөвшөөрнө. Хэт их ашиглах нь санах ойн ачааллыг нэмэгдүүлж, улмаар таны Android TV төхөөрөмжийг удаан эсвэл тогтворгүй болгож болзошгүй.""Апп нь өргөн дамжуулал дууссаны дараа үлдсэн өргөн дамжуулалыг илгээх боломжтой. Ихээр ашиглах нь хэт их санах ой ашиглан утсыг удаашруулах болон тогтворгүй болгох боломжтой.""өөрийн харилцагчдыг унших""Аппaд таны таблет дээр хадгалагдсан харилцагчдын өгөгдлийг уншихыг зөвшөөрнө. Мөн аппууд нь таны таблет дээрх харилцагч үүсгэсэн бүртгэлд хандах боломжтой байна. Үүнд таны суулгасан аппуудын үүсгэсэн бүртгэлийг оролцуулж болзошгүй. Энэ зөвшөөрөл нь аппуудад таны харилцагчийн өгөгдлийг хадгалахыг зөвшөөрөх бөгөөд хортой аппууд нь танд мэдэгдэлгүйгээр харилцагчийн өгөгдлийг хуваалцаж болзошгүй."
- "Аппaд таны Android TВ төхөөрөмж дээр хадгалагдсан харилцагчдын өгөгдлийг уншихыг зөвшөөрнө. Мөн аппууд нь таны Android TВ төхөөрөмж дээрх харилцагч үүсгэсэн бүртгэлд хандах боломжтой байна. Үүнд таны суулгасан аппуудын үүсгэсэн бүртгэлийг оролцуулж болзошгүй. Энэ зөвшөөрөл нь аппуудад таны харилцагчийн өгөгдлийг хадгалахыг зөвшөөрөх бөгөөд хортой аппууд нь танд мэдэгдэлгүйгээр харилцагчийн өгөгдлийг хуваалцаж болзошгүй."
+ "Аппaд таны Android TV төхөөрөмж дээр хадгалагдсан харилцагчдын өгөгдлийг уншихыг зөвшөөрнө. Мөн аппууд нь таны Android TV төхөөрөмж дээрх харилцагч үүсгэсэн бүртгэлд хандах боломжтой байна. Үүнд таны суулгасан аппуудын үүсгэсэн бүртгэлийг оролцуулж болзошгүй. Энэ зөвшөөрөл нь аппуудад таны харилцагчийн өгөгдлийг хадгалахыг зөвшөөрөх бөгөөд хортой аппууд нь танд мэдэгдэлгүйгээр харилцагчийн өгөгдлийг хуваалцаж болзошгүй.""Аппaд таны утсан дээр хадгалагдсан харилцагчдын өгөгдлийг уншихыг зөвшөөрнө. Мөн аппууд нь таны утсан дээрх харилцагч үүсгэсэн бүртгэлд хандах боломжтой байна. Үүнд таны суулгасан аппуудын үүсгэсэн бүртгэлийг оролцуулж болзошгүй. Энэ зөвшөөрөл нь аппуудад таны харилцагчийн өгөгдлийг хадгалахыг зөвшөөрөх бөгөөд хортой аппууд нь танд мэдэгдэлгүйгээр харилцагчийн өгөгдлийг хуваалцаж болзошгүй.""таны харилцагчдыг өөрчлөх""Аппад таны таблет дээр хадгалагдсан харилцагчдын өгөгдлийг өөрчлөхийг зөвшөөрнө. Энэ зөвшөөрөл нь харилцагчийн өгөгдлийг устгахыг аппуудад зөвшөөрнө."
- "Аппaд таны Android TВ төхөөрөмж дээр хадгалагдсан харилцагчдын өгөгдлийг өөрчлөхийг зөвшөөрнө. Энэ зөвшөөрөл нь харилцагчийн өгөгдлийг устгахыг аппуудад зөвшөөрнө."
+ "Аппaд таны Android TV төхөөрөмж дээр хадгалагдсан харилцагчдын өгөгдлийг өөрчлөхийг зөвшөөрнө. Энэ зөвшөөрөл нь харилцагчийн өгөгдлийг устгахыг аппуудад зөвшөөрнө.""Аппад таны утсан дээр хадгалагдсан харилцагчдын өгөгдлийг өөрчлөхийг зөвшөөрнө. Энэ зөвшөөрөл нь харилцагчийн өгөгдлийг устгахыг аппуудад зөвшөөрнө.""дуудлагын логийг унших""Энэ апп таны дуудлагын түүхийг унших боломжтой.""дуудлагын логруу бичих""Апп нь таны таблетын ирсэн гарсан дуудлага зэргийг агуулсан дуудлагын логыг унших боломжтой. Хортой апп нь энийг ашиглан таны дуудлагын логыг өөрчлөх болон арилгах боломжтой."
- "Аппад таны Android ТВ төхөөрөмжийн ирсэн болон залгасан дуудлага зэрэг өгөгдөл бүхий дуудлагын жагсаалтыг өөрчлөхийг зөвшөөрнө. Хортой аппууд үүнийг ашиглан таны дуудлагын жагсаалтыг устгаж эсвэл өөрчилж болзошгүй."
+ "Аппад таны Android TV төхөөрөмжийн ирсэн болон залгасан дуудлага зэрэг өгөгдөл бүхий дуудлагын жагсаалтыг өөрчлөхийг зөвшөөрнө. Хортой аппууд үүнийг ашиглан таны дуудлагын жагсаалтыг устгаж эсвэл өөрчилж болзошгүй.""Апп нь таны утасны ирсэн гарсан дуудлага зэргийг агуулсан дуудлагын логыг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан таны дуудлагын логыг өөрчлөх болон арилгах боломжтой.""биеийн мэдрэгчид хандах (зүрхний хэмнэл шалгагч г.м)""Апп-т таны зүрхний цохилт гэх мэт биеийн байдлыг хянадаг мэдрэгчдийн датанд хандалт хийх боломж олгоно.""Хуанлийн арга хэмжээ, дэлгэрэнгүйг унших""Энэ апп таны таблетад хадгалсан хуанлийн бүх арга хэмжээг унших, хуанлийн өгөгдлийг хуваалцах, хадгалах боломжтой."
- "Энэ апп таны Android ТВ төхөөрөмжид хадгалсан календарийн бүх арга хэмжээг унших болон таны календарийн өгөгдлийг хуваалцах эсвэл хадгалах боломжтой."
+ "Энэ апп таны Android TV төхөөрөмжид хадгалсан календарийн бүх арга хэмжээг унших болон таны календарийн өгөгдлийг хуваалцах эсвэл хадгалах боломжтой.""Энэ апп таны утсанд хадгалсан хуанлийн бүх арга хэмжээг унших, хуанлийн өгөгдлийг хуваалцах, хадгалах боломжтой.""календарын хуваарийг нэмэх эсвэл өөрчлөх болон эзэмшигчид мэдэгдэлгүйгээр зочидруу имэйл илгээх""Энэ апп таны таблет дээр хуанлийн арга хэмжээг нэмэх, устгах, эсвэл өөрчлөх боломжтой. Энэ апп нь хуанли эзэмшигчээс зурвас илгээсэн мэт харагдах, эсвэл эзэмшигчид мэдэгдэлгүйгээр арга хэмжээг өөрчлөх боломжтой."
- "Энэ апп таны Android ТВ төхөөрөмжид календарийн арга хэмжээ нэмэх, үүнийг устгах, эсвэл өөрчлөх боломжтой. Энэ апп календарийн өмчлөгчөөс ирсэн мэт харагдаж болох мессеж илгээх эсвэл арга хэмжээг өмчлөгчид нь мэдэгдэлгүйгээр өөрчлөх боломжтой."
+ "Энэ апп таны Android TV төхөөрөмжид календарийн арга хэмжээ нэмэх, үүнийг устгах, эсвэл өөрчлөх боломжтой. Энэ апп календарийн өмчлөгчөөс ирсэн мэт харагдаж болох мессеж илгээх эсвэл арга хэмжээг өмчлөгчид нь мэдэгдэлгүйгээр өөрчлөх боломжтой.""Энэ апп таны утсанд хуанлийн арга хэмжээг нэмэх, устгах, эсвэл өөрчлөх боломжтой. Энэ апп нь хуанли эзэмшигчээс зурвас илгээсэн мэт харагдах, эсвэл эзэмшигчид мэдэгдэлгүйгээр арга хэмжээг өөрчлөх боломжтой.""байршил нийлүүлэгчийн нэмэлт тушаалд хандах""Апп нь байршил нийлүүлэгчийн нэмэлт тушаалд хандах боломжтой. Энэ нь апп-д GPS эсвэл бусад байршлын үйлчилгээний ажиллагаанд нөлөөлөх боломжийг олгоно."
@@ -464,15 +462,15 @@
"Төхөөрөмжийн утасны дугаарт хандах зөвшөөрлийг апп-д олгоно.""машины дэлгэцийг асаалттай байлгах""таблетыг унтуулахгүй байлгах"
- "таны Android TВ төхөөрөмжийг идэвхгүй болохоос сэргийлэх"
+ "таны Android TV төхөөрөмжийг идэвхгүй болохоос сэргийлэх""утсыг унтуулахгүй байлгах""Аппад машины дэлгэцийг асаалттай байлгахыг зөвшөөрдөг.""Апп нь таблетыг унтахаас сэргийлэх боломжтой"
- "Аппад таны Android ТВ төхөөрөмжийг идэвхгүй болохоос сэргийлэхийг зөвшөөрнө."
+ "Аппад таны Android TV төхөөрөмжийг идэвхгүй болохоос сэргийлэхийг зөвшөөрнө.""Апп нь утсыг унтахаас сэргийлэх боломжтой""хэт улаанаар дамжуулах""Апп-д таблетын хэт улаан дамжуулагчийг ашиглахыг зөвшөөрнө."
- "Аппад таны Android ТВ төхөөрөмжийн хэт улаан туяаны дамжуулагчийг ашиглахыг зөвшөөрнө."
+ "Аппад таны Android TV төхөөрөмжийн хэт улаан туяаны дамжуулагчийг ашиглахыг зөвшөөрнө.""Апп-д утасны хэт улаан дамжуулагчийг ашиглахыг зөвшөөрнө.""ханын зургийг тохируулах""Апп нь системийн ханын зургийг тохируулах боломжтой."
@@ -480,11 +478,11 @@
"Апп нь системийн ханын зургийн хэмжээний саналыг тохируулах боломжтой""цагийн бүсийн тохиргоо""Апп нь таблетын цагийн бүсийг солих боломжтой."
- "Аппад таны Android ТВ төхөөрөмжийн цагийн бүсийг өөрчлөхийг зөвшөөрнө."
+ "Аппад таны Android TV төхөөрөмжийн цагийн бүсийг өөрчлөхийг зөвшөөрнө.""Апп нь утасны цагийн бүсийг өөрчлөх боломжтой.""төхөөрөмж дээрх акаунтыг олох""Апп нь таблетэд мэдэгдэж байгаа бүртгэлийн жагсаалтыг авах боломжтой. Энд таны суулгасан аппликейшнүүдийг үүсгэсэн бүх акаунтууд хамрагдана."
- "Аппад таны Android ТВ төхөөрөмжөөр танигдсан бүртгэлийн жагсаалтыг авахыг зөвшөөрнө. Үүнд таны суулгасан аппуудын үүсгэсэн аливаа бүртгэлийг оруулж болзошгүй."
+ "Аппад таны Android TV төхөөрөмжөөр танигдсан бүртгэлийн жагсаалтыг авахыг зөвшөөрнө. Үүнд таны суулгасан аппуудын үүсгэсэн аливаа бүртгэлийг оруулж болзошгүй.""Апп нь утсанд мэдэгдэж байгаа бүртгэлийн жагсаалтыг авах боломжтой. Энд таны суулгасан аппликейшнүүдийг үүсгэсэн бүх акаунтууд хамрагдана.""сүлжээний холболтыг үзэх""Апп нь сүлжээ байгаа болон холбогдсон эсэх зэрэг сүлжээний холболтын талаарх мэдээллийг харах боломжтой."
@@ -500,21 +498,21 @@
"Апп нь Wi-Fi холболтын цэгтэй холбогдох буюу салах боломжтой ба тохируулсан Wi-Fi сүлжээнд өөрчлөлт хийх боломжтой.""Wi-Fi олон дамжуулалт хүлээн авахыг зөвшөөрөх""Апп нь олон дамжуулал ашиглан Wi-Fi сүлжээн дэх бүх төхөөрөмжрүү пакет илгээх болон хүлээн авах боломжтой. Энэ нь олон дамжуулал ашиглахгүй горимоос илүү их тэжээл зарцуулна."
- "Аппад таны Android ТВ төхөөрөмжөөс гадна Wi-Fi сүлжээг ашиглаж буй бүх төхөөрөмжид илгээсэн багцыг олон цэгийн хаяг ашиглан хүлээн авахыг зөвшөөрнө. Энэ нь олон цэгийн бус горимоос илүү их тэжээл ашиглана."
+ "Аппад таны Android TV төхөөрөмжөөс гадна Wi-Fi сүлжээг ашиглаж буй бүх төхөөрөмжид илгээсэн багцыг олон цэгийн хаяг ашиглан хүлээн авахыг зөвшөөрнө. Энэ нь олон цэгийн бус горимоос илүү их тэжээл ашиглана.""Апп нь олон дамжуулал ашиглан Wi-Fi сүлжээн дэх бүх төхөөрөмжрүү пакет илгээх болон хүлээн авах боломжтой. Энэ нь олон дамжуулал ашиглахгүй горимоос илүү их тэжээл зарцуулна.""Bluetooth тохиргоонд хандах""Апп нь дотоод блютүүт таблетын тохиргоог харах боломжтой ба хос болох төхөөрөмжтэй холболтыг зөвшөөрөх болон хийх боломжтой"
- "Аппад таны Android ТВ төхөөрөмжийн Bluetooth-г тохируулах болон алсын төхөөрөмжүүдийг илрүүлж, тэдгээртэй хослуулахыг зөвшөөрнө."
+ "Аппад таны Android TV төхөөрөмжийн Bluetooth-г тохируулах болон алсын төхөөрөмжүүдийг илрүүлж, тэдгээртэй хослуулахыг зөвшөөрнө.""Апп нь утасны дотоод блютүүтыг тохируулах боломжтой ба гадаад төхөөрөмжийг олох болон хос үүсгэх боломжтой.""WiMAX-д холбогдох болон салах""Апп нь WiMAX идэвхтэй эсэх болон холбогдсон WiMAX сүлжээний талаар мэдээллийг тодорхойлох боломжтой.""WiMAX статусыг солих""Апп нь WiMAX сүлжээнд таблетыг холбох болон салгах боломжтой."
- "Аппад таны Android ТВ төхөөрөмжид холбогдож, үүнийг WiMAX сүлжээнээс салгахыг зөвшөөрнө."
+ "Аппад таны Android TV төхөөрөмжид холбогдож, үүнийг WiMAX сүлжээнээс салгахыг зөвшөөрнө.""Апп нь WiMAX сүлжээнд утсыг холбох болон салгах боломжтой.""Bluetooth төхөөрөмжтэй хос үүсгэх""Апп нь таблет дээрх блютүүт тохиргоог харах боломжтой ба хос болох төхөөрөмжтэй холболтыг зөвшөөрөх болон хийх боломжтой."
- "Аппад таны Android ТВ төхөөрөмж дээрх Bluetooth-н тохируулгыг харах болон хослуулсан төхөөрөмжүүдтэй холболт хийж, холболтыг баталгаажуулахыг зөвшөөрнө."
+ "Аппад таны Android TV төхөөрөмж дээрх Bluetooth-н тохируулгыг харах болон хослуулсан төхөөрөмжүүдтэй холболт хийж, холболтыг баталгаажуулахыг зөвшөөрнө.""Апп нь утсан дээрх Bluetooth тохиргоог харах боломжтой ба хос болох төхөөрөмжтэй холболтыг зөвшөөрөх болон хийх боломжтой.""Сонгосон NFC төлбөрийн үйлчилгээний мэдээлэл""Бүртгүүлсэн төхөөрөмж болон маршрутын хүрэх цэг зэрэг сонгосон nfc төлбөрийн үйлчилгээний мэдээллийг авахыг аппад зөвшөөрдөг."
@@ -675,10 +673,10 @@
"Дэлгэц түгжих нууц үг болон ПИН кодны урт болон нийт тэмдэгтийн уртыг хянах.""Дэлгэцийн түгжээг тайлах оролдлогыг хянах""Дэлгэц түгжигдсэн үед нууц үг буруу оруулалтын тоог хянах ба хэрэв хэт олон удаа нууц үгийг буруу оруулбал таблетыг түгжих болон таблетын бүх датаг арилгана"
- "Дэлгэцийн түгжээг тайлахаар буруу оруулсан нууц үгийн тоог хянаж, нууц үгийг хэт олон удаа буруу оруулсан тохиолдолд таны Android ТВ төхөөрөмжийг түгжиж эсвэл үүний бүх өгөгдлийг устгана."
+ "Дэлгэцийн түгжээг тайлахаар буруу оруулсан нууц үгийн тоог хянаж, нууц үгийг хэт олон удаа буруу оруулсан тохиолдолд таны Android TV төхөөрөмжийг түгжиж эсвэл үүний бүх өгөгдлийг устгана.""Дэлгэц түгжигдсэн үед нууц үг буруу оруулалтын тоог хянах, ба хэрэв хэт олон удаа нууц үгийг буруу оруулбал утсыг түгжих болон утасны бүх датаг арилгана""Дэлгэцийн түгжээг тайлахад оруулсан буруу нууц үгийн давтамжийг хянаж таблетыг түгжих эсвэл буруу нууц үгийг хэт олон удаа оруулсан тохиолдолд энэ хэрэглэгчийн мэдээллийг устгах."
- "Дэлгэцийн түгжээг тайлахаар буруу оруулсан нууц үгийн тоог хянаж, нууц үгийг хэт олон удаа буруу оруулсан тохиолдолд таны Android ТВ төхөөрөмжийг түгжиж эсвэл энэ хэрэглэгчийн бүх өгөгдлийг устгана."
+ "Дэлгэцийн түгжээг тайлахаар буруу оруулсан нууц үгийн тоог хянаж, нууц үгийг хэт олон удаа буруу оруулсан тохиолдолд таны Android TV төхөөрөмжийг түгжиж эсвэл энэ хэрэглэгчийн бүх өгөгдлийг устгана.""Дэлгэцийн түгжээг тайлахад оруулсан буруу нууц үгийн давтамжийг хянаж гар утсыг түгжих эсвэл буруу нууц үгийг хэт олон удаа оруулсан тохиолдолд энэ хэрэглэгчийн мэдээллийг устгах.""Дэлгэцийн түгжээг өөрчлөх""Дэлгэцийн түгжээг өөрчлөх."
@@ -686,11 +684,11 @@
"Дэлгэц хэзээ яаж түгжихийг удирдах""Бүх датаг арилгах""Үйлдвэрийн дата утгыг өгсөнөөр таблетын дата шууд арилгагдана."
- "Таны Android ТВ төхөөрөмжийн өгөгдлийг танд анхааруулалгүйгээр үйлдвэрээс гарсан төлөвт шилжүүлэн устгана."
+ "Таны Android TV төхөөрөмжийн өгөгдлийг танд анхааруулалгүйгээр үйлдвэрээс гарсан төлөвт шилжүүлэн устгана.""Сануулахгүйгээр утасны бүх мэдээллийг устгаж, үйлдвэрийн өгөгдмөл байдалд шилжүүлнэ""Хэрэглэгчийн мэдээллийг арилгах""Анхааруулга өгөхгүйгээр энэ хэрэглэгчийн энэ таблет дээрх мэдээллийг устгах."
- "Энэ Android ТВ төхөөрөмж дээрх хэрэглэгчийн өгөгдлийг хэрэглэгчид анхааруулалгүйгээр устгана."
+ "Энэ Android TV төхөөрөмж дээрх хэрэглэгчийн өгөгдлийг хэрэглэгчид анхааруулалгүйгээр устгана.""Анхааруулга өгөхгүйгээр энэ хэрэглэгчийн энэ гар утсан дээрх мэдээллийг устгах.""Төхөрөөмжийн глобал проксиг тохируулах""Бодлогыг ашиглах боломжтой үед төхөөрөмжийн олон улсын эрхийг тохируулах. Зөвхөн төхөөрөмж эзэмшигч нь олон улсын эрхийг тохируулах боломжтой."
@@ -840,7 +838,7 @@
"Нүүрээр түгжээ тайлах оролдлогын тоо дээд хэмжээнээс хэтэрсэн""SIM карт байхгүй""Таблет SIM картгүй."
- "Таны Android ТВ төхөөрөмжид SIM карт алга."
+ "Таны Android TV төхөөрөмжид SIM карт алга.""Утсанд SIM карт байхгүй.""SIM картыг оруулна уу.""SIM карт байхгүй эсвэл унших боломжгүй. SIM карт оруулна уу."
@@ -863,13 +861,13 @@
"Та нууц үгээ %1$d удаа буруу бичив. \n\n%2$d секундын дараа дахин оролдоно уу.""Та PIN кодоо %1$d удаа буруу бичив. \n\n%2$d секундын дараа дахин оролдоно уу.""Та тайлах хээг %1$d удаа буруу зурлаа. %2$d удаа дахин буруу оруулбал, та таблетаа тайлахын тулд Google нэвтрэлтээ ашиглах шаардлагатай болно.\n\n %3$d секундын дараа дахин оролдоно уу."
- "Та тайлах хээг %1$d удаа буруу зурсан байна. Та дахин %2$d удаа буруу оруулсан тохиолдолд Android ТВ төхөөрөмжийнхөө түгжээг тайлахын тулд Google-д нэвтрэх шаардлагатай болно.\n\n %3$d секундийн дараа дахин оролдоно уу."
+ "Та тайлах хээг %1$d удаа буруу зурсан байна. Та дахин %2$d удаа буруу оруулсан тохиолдолд Android TV төхөөрөмжийнхөө түгжээг тайлахын тулд Google-д нэвтрэх шаардлагатай болно.\n\n %3$d секундийн дараа дахин оролдоно уу.""Та тайлах хээг %1$d удаа буруу зурлаа. %2$d удаа дахин буруу оролдвол, та таблетаа тайлахын тулд Google нэвтрэлтээ ашиглах шаардлагатай болно.\n\n %3$d секундын дараа дахин оролдоно уу.""Та таблетыг тайлах гэж %1$d удаа буруу оролдлоо. %2$d удаа дахин буруу оролдвол таблет үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана."
- "Та Android ТВ төхөөрөмжийнхөө түгжээг тайлахаар %1$d удаа буруу оролдсон байна. %2$d удаагийн амжилтгүй оролдлогын дараагаас таны Android ТВ төхөөрөмжийг үйлдвэрийн өгөгдмөл төлөвт шинэчлэх бөгөөд хэрэглэгчийн бүх өгөгдөл устах болно."
+ "Та Android TV төхөөрөмжийнхөө түгжээг тайлахаар %1$d удаа буруу оролдсон байна. %2$d удаагийн амжилтгүй оролдлогын дараагаас таны Android TV төхөөрөмжийг үйлдвэрийн өгөгдмөл төлөвт шинэчлэх бөгөөд хэрэглэгчийн бүх өгөгдөл устах болно.""Та утсыг тайлах гэж %1$d удаа буруу оролдлоо. %2$d удаа дахин буруу оролдвол утас үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана.""Та таблетыг %d удаа тайлах гэж буруу оролдлоо. Таблет одоо үйлдвэрийн үндсэн утгаараа тохируулагдах болно."
- "Та Android ТВ төхөөрөмжийнхөө түгжээг тайлахаар %d удаа буруу оролдсон байна. Таны Android ТВ төхөөрөмжийг одоо үйлдвэрийн өгөгдмөл төлөвт шинэчилнэ."
+ "Та Android TV төхөөрөмжийнхөө түгжээг тайлахаар %d удаа буруу оролдсон байна. Таны Android TV төхөөрөмжийг одоо үйлдвэрийн өгөгдмөл төлөвт шинэчилнэ.""Та утсыг тайлах гэж %d удаа буруу оролдлоо. Утас одоо үйлдвэрийн үндсэн утгаараа тохируулагдах болно.""%d секундын дараа дахин оролдоно уу.""Хээг мартсан уу?"
@@ -956,7 +954,7 @@
"Апп нь Хөтчийн зочилж байсан бүх URL-н түүх болон Хөтчийн бүх хавчуургыг унших боломжтой. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадавхтай аппликейшнүүдэд ашиглагдахгүй байх боломжтой.""вэб хавчуурга болон түүхийг бичих""Апп нь таны таблет дээр хадгалагдсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөх боломжтой. Энэ нь апп-д Хөтчийн датаг арилгах эсвэл өөрчлөх боломжийг олгоно. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадвартай аппликейшнд ажиллахгүй байх боломжтой."
- "Аппад таны Android ТВ төхөөрөмжид хадгалсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөхийг зөвшөөрнө. Энэ нь аппад Хөтчийн өгөгдлийг устгах эсвэл өөрчлөхийг зөвшөөрч болзошгүй. Санамж: энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вэб хөтчийн чадамжтай бусад аппад хэрэгжихгүй байж болзошгүй."
+ "Аппад таны Android TV төхөөрөмжид хадгалсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөхийг зөвшөөрнө. Энэ нь аппад Хөтчийн өгөгдлийг устгах эсвэл өөрчлөхийг зөвшөөрч болзошгүй. Санамж: энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вэб хөтчийн чадамжтай бусад аппад хэрэгжихгүй байж болзошгүй.""Апп нь таны утсан дээр хадгалагдсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөх боломжтой. Энэ нь апп-д Хөтчийн датаг арилгах эсвэл өөрчлөх боломжийг олгоно. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадвартай аппликейшнд ажиллахгүй байх боломжтой.""сэрүүлэг тохируулах""Апп нь суулгагдсан сэрүүлэгний апп дээр сэрүүлэг тохируулах боломжтой. Зарим сэрүүлэгний апп нь энэ функцийг дэмжихгүй байж болзошгүй."
@@ -1611,27 +1609,26 @@
"Та PIN кодоо %1$d удаа буруу бичив. \n\n%2$d секундын дараа дахин оролдоно уу.""Та тайлах хээг %1$d удаа буруу зурлаа. \n\n%2$d секундын дараа дахин оролдоно уу.""Та таблетыг тайлах гэж %1$d удаа буруу оролдлоо. %2$d удаа дахин буруу оролдвол таблет үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана."
- "Та Android ТВ төхөөрөмжийнхөө түгжээг тайлахаар %1$d удаа буруу оролдсон байна. %2$d удаагийн амжилтгүй оролдлогын дараагаас таны Android ТВ төхөөрөмжийг үйлдвэрийн өгөгдмөл төлөвт шинэчлэх бөгөөд хэрэглэгчийн бүх өгөгдөл устах болно."
+ "Та Android TV төхөөрөмжийнхөө түгжээг тайлахаар %1$d удаа буруу оролдсон байна. %2$d удаагийн амжилтгүй оролдлогын дараагаас таны Android TV төхөөрөмжийг үйлдвэрийн өгөгдмөл төлөвт шинэчлэх бөгөөд хэрэглэгчийн бүх өгөгдөл устах болно.""Та утсыг тайлах гэж %1$d удаа буруу оролдлоо. %2$d удаа дахин буруу оролдвол утас үйлдвэрийн үндсэн утгаараа тохируулагдах ба хэрэглэгчийн дата бүхэлдээ устана.""Та таблетыг тайлах гэж %d удаа буруу оролдлоо. Таблет одоо үйлдвэрийн үндсэн утгаараа тохируулагдах болно."
- "Та Android ТВ төхөөрөмжийнхөө түгжээг тайлахаар %d удаа буруу оролдсон байна. Таны Android ТВ төхөөрөмжийг одоо үйлдвэрийн өгөгдмөл төлөвт шинэчилнэ."
+ "Та Android TV төхөөрөмжийнхөө түгжээг тайлахаар %d удаа буруу оролдсон байна. Таны Android TV төхөөрөмжийг одоо үйлдвэрийн өгөгдмөл төлөвт шинэчилнэ.""Та утсыг тайлах гэж %d удаа буруу оролдлоо. Утас одоо үйлдвэрийн үндсэн утгаараа тохируулагдах болно.""Та тайлах хээг %1$d удаа буруу зурлаа. %2$d удаа дахин буруу оруулбал, та таблетаа тайлахын тулд имэйл бүртгэл шаардлагатай болно.\n\n %3$d секундын дараа дахин оролдоно уу."
- "Та тайлах хээг %1$d удаа буруу зурсан байна. Та дахин %2$d удаа буруу оролдсон тохиолдолд Android ТВ төхөөрөмжийнхөө түгжээг имэйл бүртгэлээрээ тайлах шаардлагатай болно.\n\n %3$d секундийн дараа дахин оролдоно уу."
+ "Та тайлах хээг %1$d удаа буруу зурсан байна. Та дахин %2$d удаа буруу оролдсон тохиолдолд Android TV төхөөрөмжийнхөө түгжээг имэйл бүртгэлээрээ тайлах шаардлагатай болно.\n\n %3$d секундийн дараа дахин оролдоно уу.""Та тайлах хээг %1$d удаа буруу зурлаа. %2$d удаа дахин буруу оруулбал, та утсаа тайлахын тулд имэйл бүртгэлээ ашиглах шаардлагатай болно.\n\n %3$d секундын дараа дахин оролдоно уу."" — ""Устгах"
- "%1$s-н дэлгэц дээрх үйлчилгээг эхлүүлдэг дэвсгэр нь цаашид R хийцийн ашиглах үеийн зөвшөөрөлгүй болно. go/r-bg-fgs-restriction-г үзэж, алдааны мэдээ илгээнэ үү.""Дууг санал болгосноос чанга болгож өсгөх үү?\n\nУрт хугацаанд чанга хөгжим сонсох нь таны сонсголыг муутгаж болно.""Хүртээмжийн товчлолыг ашиглах уу?""Товчлол асаалттай үед дууны түвшний хоёр товчлуурыг хамтад нь 3 секунд дарснаар хандалтын онцлогийг эхлүүлнэ.""Хандалтын онцлогуудыг асаах уу?"
- "Дууны түвшний түлхүүрийг хэдэн секундийн турш зэрэг дарах нь хандалтын онцлогуудыг асаадаг. Энэ нь таны төхөөрөмжийн ажиллах зарчмыг өөрчилж болзошгүй.\n\nОдоогийн онцлогууд:\n%1$s\nТа сонгосон онцлогуудыг Тохиргоо > Хандалт хэсэгт өөрчлөх боломжтой."
+ "Дууны түвшний түлхүүрийг хэдэн секундийн турш зэрэг дарснаар хандалтын онцлогууд асна. Энэ нь таны төхөөрөмжийн ажиллах зарчмыг өөрчилж болзошгүй.\n\nОдоогийн онцлогууд:\n%1$s\nТа сонгосон онцлогуудыг Тохиргоо > Хандалт хэсэгт өөрчлөх боломжтой."" • %1$s\n""%1$s-г асаах уу?""Дууны түвшний түлхүүрийг хэдэн секундийн турш зэрэг дарах нь хандалтын онцлог болох %1$s-г асаадаг. Энэ нь таны төхөөрөмжийн ажиллах зарчмыг өөрчилж болзошгүй.\n\nТа Тохиргоо > Хандалт хэсэгт энэ товчлолыг өөр онцлогт оноож өөрчлөх боломжтой.""Асаах"
- "Битгий асаа"
+ "Бүү асаа""ИДЭВХТЭЙ""ИДЭВХГҮЙ""%1$s-д таны төхөөрөмжийг бүрэн хянахыг зөвшөөрөх үү?"
@@ -1788,7 +1785,7 @@
"Ажлын %1$s""2 дахь ажил %1$s""3 дахь ажил %1$s"
- "Тогтоосныг суллахаас өмнө PIN асуух"
+ "Бэхэлснийг болиулахаасаа өмнө PIN асуух""Тогтоосныг суллахаас өмнө түгжээ тайлах хээ асуух""Тогтоосныг суллахаас өмнө нууц үг асуух""Таны админ суулгасан"
@@ -1843,7 +1840,7 @@
"Ажлын өдрийн шөнө""Амралтын өдөр""Үйл явдал"
- "Идэвхгүй"
+ "Унтлагын цаг""%1$s зарим дууны дууг хааж байна""Таны төхөөрөмжид дотоод алдаа байна.Та төхөөрөмжөө үйлдвэрээс гарсан төлөвт шилжүүлэх хүртэл таны төхөөрөмж чинь тогтворгүй байж болох юм.""Таны төхөөрөмжид дотоод алдаа байна. Дэлгэрэнгүй мэдээлэл авахыг хүсвэл үйлдвэрлэгчтэйгээ холбоо барина уу."
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index a7e6f99ba8fd6b2e29923c977f5dedcc05443244..bc4450fef4ff737faaeca5e55a0ab292961d785d 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -202,10 +202,8 @@
"%s नी प्रिंट करणे बंद केले आहे.""तुमची कार्य प्रोफाइल सुरू करा""तुम्ही तुमची कार्य प्रोफाइल सुरू करेपर्यंत तुमची वैयक्तिक ॲप्स ब्लॉक केली आहेत"
-
-
-
-
+ "वैयक्तिक ॲप्स %1$s रोजी %2$s वाजता ब्लॉक केली जातील. तुमचा आयटी अॅडमिन तुमची कार्य प्रोफाइल %3$d पेक्षा जास्त दिवसांसाठी बंद ठेवण्याची अनुमती देत नाही."
+ "सुरू करा""मी""टॅबलेट पर्याय""Android TV पर्याय"
@@ -1131,7 +1129,7 @@
"%1$s सह उघडा""उघडा""वापरून %1$s लिंक उघडा"
- "वापरून लिंक उघडा"
+ "हे वापरून लिंक उघडा""%1$s वापरून लिंक उघडा""%2$s वापरून %1$s लिंक उघडा""अॅक्सेस द्या"
@@ -1621,7 +1619,6 @@
"तुम्ही तुमचा अनलॉक पॅटर्न %1$d वेळा अयोग्यपणे काढला आहे. आणखी %2$d अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून तुमचा फोन अनलॉक करण्यास सांगितले जाईल.\n\n %3$d सेकंदांमध्ये पुन्हा प्रयत्न करा."" — ""काढा"
- "%1$s कडून बॅकग्राउंडने फोरग्राउंडमध्ये सुरू केलेल्या सेवेला भविष्यातील आर बिल्डमध्ये वापर करते वेळची परवानगी नसेल. कृपया go/r-bg-fgs-restriction पहा आणि बगची तक्रार नोंदवा.""शिफारस केलेल्या पातळीच्या वर आवाज वाढवायचा?\n\nउच्च आवाजात दीर्घ काळ ऐकण्याने आपल्या श्रवणशक्तीची हानी होऊ शकते.""प्रवेशयोग्यता शॉर्टकट वापरायचा?""शॉर्टकट सुरू असताना, दोन्ही व्हॉल्यूम बटणे तीन सेकंदांसाठी दाबून ठेवल्याने अॅक्सेसिबिलिटी वैशिष्ट्य सुरू होईल."
@@ -1654,7 +1651,7 @@
"रंगांची उलटापालट""रंग सुधारणा""धरून ठेवलेल्या व्हॉल्यूम की. %1$s सुरू केला आहे."
- "धरून ठेवलेल्या व्हॉल्यूम की. %1$s बंद केला आहे."
+ "धरून ठेवलेल्या व्हॉल्यूम की. %1$s बंद केले आहे.""%1$s वापरण्यासाठी दोन्ही व्हॉल्युम की तीन सेकंद दाबा आणि धरून ठेवा""तुम्ही अॅक्सेसिबिलिटी बटणावर टॅप केल्यास वापरण्यासाठी एक वैशिष्ट्य निवडा:""अॅक्सेसिबिलिटी जेश्चरसोबत वापराचे असलेले वैशिष्ट्य निवडा (दोन बोटांनी स्क्रीनच्या तळापासून वर स्वाइप करा):"
@@ -2032,7 +2029,7 @@
%s + %d फाइल%s + %d फाइल
- "शेअर करण्यासाठी कोणतीही शिफारस केलेले लोक नाहीत"
+ "शेअर करण्यासाठी शिफारस केलेल्या कोणत्याही व्यक्ती नाहीत""अॅप्स सूची""या अॅपला रेकॉर्ड करण्याची परवानगी दिली गेली नाही पण हे USB डिव्हाइस वापरून ऑडिओ कॅप्चर केला जाऊ शकतो.""होम"
@@ -2054,21 +2051,21 @@
"गट संभाषण""%1$d+""वैयक्तिक"
- "ऑफिस"
+ "कार्य""वैयक्तिक दृश्य""कार्य दृश्य"
- "हे ऑफिस ॲप्ससोबत शेअर करू शकत नाही"
+ "हे कार्य ॲप्ससोबत शेअर करू शकत नाही""तुमचा IT अॅडमिन तुम्हाला हा आशय तुमच्या ऑफिस प्रोफाइलमधील अॅप्ससोबत शेअर करू देत नाही""हे ऑफिस अॅप्ससोबत उघडू शकत नाही"
- "तुमचा IT अॅडमिन तुम्हाला हा आशय तुमच्या ऑफिस प्रोफाइलमधील अॅप्ससोबत उघडू देत नाही"
+ "तुमचा IT अॅडमिन तुम्हाला हा आशय तुमच्या कार्य प्रोफाइलमधील अॅप्सनी उघडू देत नाही""हे वैयक्तिक अॅप्ससोबत शेअर करू शकत नाही""तुमचा IT अॅडमिन तुम्हाला हा आशय तुमच्या वैयक्तिक प्रोफाइलमधील अॅप्ससोबत शेअर करू देत नाही""हे वैयक्तिक अॅप्ससोबत उघडू शकत नाही""तुमचा IT अॅडमिन तुम्हाला हा आशय तुमच्या वैयक्तिक प्रोफाइलमधील अॅप्ससोबत उघडू देत नाही"
- "ऑफिस प्रोफाइल थांबवली आहे"
+ "कार्य प्रोफाइल थांबवली आहे""सुरू करा"
- "ऑफिस अॅप्स या आशयाला सपोर्ट करू शकत नाही"
- "ऑफिस अॅप्स हा आशय उघडू शकत नाही"
+ "कार्य अॅप्स या आशयाला सपोर्ट करू शकत नाही"
+ "कार्य अॅप्स हा आशय उघडू शकत नाही""वैयक्तिक अॅप्स या आशयाला सपोर्ट करू शकत नाही""वैयक्तिक अॅप्स हा आशय उघडू शकत नाही""सिम नेटवर्क अनलॉक पिन"
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 5d075fa8248741a29cf2072660506cf05bd7b069..6d2d060d645183b5124b1880300e8630d913fb1d 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -202,10 +202,8 @@
"Pencetakan dilumpuhkan oleh %s.""Hidupkan profil kerja anda""Apl peribadi anda disekat sehingga anda menghidupkan profil kerja"
-
-
-
-
+ "Apl peribadi akan disekat pada %1$s pukul %2$s. Pentadbir IT anda tidak membenarkan profil kerja anda dimatikan melebihi %3$d hari."
+ "Hidupkan""Saya""Pilihan tablet""Pilihan Android TV"
@@ -1621,7 +1619,6 @@
"Anda telah tersilap lukis corak buka kunci sebanyak %1$d kali. Selepas %2$d lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci telefon anda menggunakan log masuk Google anda.\n\n Cuba lagi dalam %3$d saat."" — ""Alih keluar"
- "Perkhidmatan latar depan dimulakan latar belakang daripada %1$s tidak akan mempunyai kebenaran semasa-dalam-penggunaan dalam binaan R akan datang. Sila lihat go/r-bg-fgs-restriction dan failkan laporan pepijat.""Naikkan kelantangan melebihi paras yang disyokorkan?\n\nMendengar pada kelantangan yang tinggi untuk tempoh yang lama boleh merosakkan pendengaran anda.""Gunakan Pintasan Kebolehaksesan?""Apabila pintasan dihidupkan, tindakan menekan kedua-dua butang kelantangan selama 3 saat akan memulakan ciri kebolehaksesan."
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 87eb087f83d4271247d95b2099c2ba7b34d69056..2363860968dace05cc45fbb20900eef96e713b17 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -142,7 +142,7 @@
"Wi-Fi""WiFi ခေါ်ဆိုမှု""VoWifi"
- "ပိတ်ထားရသည်"
+ "ပိတ်ထားသည်""Wi-Fi သုံး၍ ခေါ်ဆိုသည်""မိုဘိုင်းကွန်ရက်သုံး၍ ခေါ်ဆိုသည်""ကြိုးမဲ့အင်တာနက် သာလျှင်"
@@ -202,10 +202,8 @@
"%s က ပုံနှိပ်ထုတ်ယူခြင်းကို ပိတ်ထားသည်။""သင့်အလုပ်ပရိုဖိုင် ဖွင့်ခြင်း""သင့်အလုပ်ပရိုဖိုင် ဖွင့်သည်အထိ ကိုယ်ပိုင်အက်ပ်များကို ပိတ်ထားသည်"
-
-
-
-
+ "ကိုယ်ပိုင်အက်ပ်များကို %1$s%2$s တွင် ပိတ်ပါမည်။ သင့်အလုပ်ပရိုဖိုင် %3$d ရက်ထက်ပိုပြီး ပိတ်ထားခြင်းကို သင်၏ IT စီမံခန့်ခွဲသူက ခွင့်မပြုပါ။"
+ "ဖွင့်ရန်""ကျွန်ုပ်""Tabletဆိုင်ရာရွေးချယ်မှုများ""Android TV ရွေးချယ်စရာများ"
@@ -1092,7 +1090,7 @@
"%1$02d:%2$02d""%1$d:%2$02d:%3$02d""အားလုံးရွေးရန်"
- "ဖြတ်ခြင်း"
+ "ဖြတ်ရန်""ကူးရန်""ကလစ်ဘုတ်သို့ မိတ္တူကူးခြင်း မအောင်မြင်ပါ""Paste"
@@ -1553,7 +1551,7 @@
"တစ်ခါတည်း""%1$s က အလုပ်ပရိုဖိုင်ကို မပံ့ပိုးပါ။""တက်ဘလက်"
- "တီဗွီ"
+ "TV""ဖုန်း""အထိုင်ရှိသော စပီကာများ""HDMI"
@@ -1621,7 +1619,6 @@
"သင် ပုံဖော်၍သော့ဖွင့်ခြင်းကို %1$d အကြိမ် မှန်ကန်စွာ မပြုလုပ်နိုင်ပါ။ နောက်ထပ် %2$d အကြိမ် မမှန်ကန်ပါက သင့်ဖုန်းအား အီးမေးလ်အသုံးပြု၍ သော့ဖွင့်ရန် တောင်းဆိုပါလိမ့်မည်။ \n\n %3$d စက္ကန့်အကြာတွင် ပြန်လည် ကြိုးစားပါ"" — ""ဖယ်ရှားရန်"
- "%1$s တွင် မှ စတင်သည့် foreground ဝန်ဆောင်မှုသည် နောက်ထွက်ရှိမည့် R စုပေါင်းစပ်ပေါင်း ပရိုဂရမ်များတွင် အသုံးပြုစဉ်အတွင်း ခွင့်ပြုချက် ရရှိမည်မဟုတ်ပါ။ go/r-bg-fgs-ကန့်သတ်ချက်များကို ကြည့်ပြီး အမှားသတင်းပို့ချက်တစ်ခု တင်သွင်းပါ။""အသံကို အကြံပြုထားသည့် ပမာဏထက် မြှင့်ပေးရမလား?\n\nအသံကို မြင့်သည့် အဆင့်မှာ ကြာရှည်စွာ နားထောင်ခြင်းက သင်၏ နားကို ထိခိုက်စေနိုင်သည်။""အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်ကို အသုံးပြုလိုပါသလား။""ဖြတ်လမ်းလင့်ခ်ကို ဖွင့်ထားစဉ် အသံထိန်းခလုတ် နှစ်ခုစလုံးကို ၃ စက္ကန့်ခန့် ဖိထားခြင်းဖြင့် အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုကို ဖွင့်နိုင်သည်။"
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index f61150a7f607e62dae09e0d42cad5fb21ab84966..1d15398f85394ab7eeef0d84bb718247e6a3bf61 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -202,10 +202,8 @@
"%s har slått av utskrift.""Slå på jobbprofilen din""De personlige appene dine er blokkert til du slår på jobbprofilen din"
-
-
-
-
+ "Personlige apper blir blokkert %1$s klokken %2$s. IT-administratoren din tillater ikke at jobbprofilen din er slått av i mer enn %3$d dager."
+ "Slå på""Meg""Innstillinger for nettbrettet""Android TV-alternativer"
@@ -1621,7 +1619,6 @@
"Du har tegnet opplåsningsmønsteret feil %1$d ganger. Etter ytterligere %2$d gale forsøk, blir du bedt om å låse opp telefonen via en e-postkonto.\n\n Prøv på nytt om %3$d sekunder."" — ""Fjern"
- "Forgrunnstjenesten fra %1$s, som ble startet i bakgrunnen, kommer ikke til å ha tillatelser mens den er i bruk i fremtidige R-delversjoner. Les go/r-bg-fgs-restriction og send inn en feilrapport.""Vil du øke volumet til over anbefalt nivå?\n\nHvis du hører på et høyt volum over lengre perioder, kan det skade hørselen din.""Vil du bruke tilgjengelighetssnarveien?""Når snarveien er på, starter en tilgjengelighetsfunksjon når du trykker inn begge volumknappene i tre sekunder."
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index dd27aaaaa50de83f3a6f1ac06f5347f28f61ab04..b295fdef79e7afbdca4f029cc6d56b039942fc23 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -175,7 +175,7 @@
"अति धेरै %s मेटाउने प्रयास गरियो।""ट्याब्लेट भण्डारण खाली छैन! ठाउँ खाली गर्नको लागि केही फाइलहरू मेटाउनुहोस्।""भण्डारण भरिएको छ हेर्नुहोस्। ठाउँ खाली गर्न केही फाइलहरू मेटाउनुहोस्।"
- "Android TV यन्त्रको भण्डारण भरिएको छ। ठाउँ खाली गर्न केही फाइलहरू मेट्नुहोस्।"
+ "Android टिभी यन्त्रको भण्डारण भरिएको छ। ठाउँ खाली गर्न केही फाइलहरू मेट्नुहोस्।""फोन भण्डारण भरिएको छ! ठाउँ खाली गर्नको लागि केही फाइलहरू मेटाउनुहोस्।"प्रमाणपत्रका अख्तियारीहरूलाई स्थापना गरियो
@@ -202,13 +202,11 @@
"%s ले छाप्ने कार्यलाई असक्षम पार्यो।""आफ्नो कार्य प्रोफाइल सक्रिय गर्नुहोस्""तपाईंले आफ्नो कार्य प्रोफाइल सक्रिय नगरुन्जेल तपाईंका व्यक्तिगत अनुप्रयोगहरूलाई रोक लगाइन्छ"
-
-
-
-
+ "मिति %1$s%2$s बजे व्यक्तिगत एपहरूलाई रोक लगाइने छ। तपाईंका IT एडमिन तपाईंलाई आफ्नो कार्य प्रोफाइल %3$d भन्दा धेरै दिन निष्क्रिय राख्ने अनुमति दिनुहुन्न।"
+ "सक्रिय गर्नुहोस्""मलाई""ट्याब्लेट विकल्पहरू"
- "Android TV सम्बन्धी विकल्पहरू"
+ "Android टिभी सम्बन्धी विकल्पहरू""फोन विकल्पहरू""मौन मोड""वायरलेस अन गर्नुहोस्"
@@ -226,7 +224,7 @@
"पुनःसुरु हुँदै ...""बन्द गर्दै...""तपाईँको ट्याब्लेट बन्द हुने छ।"
- "तपाईंको Android TV यन्त्र बन्द हुने छ।"
+ "तपाईंको Android टिभी यन्त्र बन्द हुने छ।""तपाईँको घडी बन्द गरिने छ।""तपाईँको फोन बन्द हुने छ।""के तपाईं बन्द गर्न चाहनुहुन्छ?"
@@ -235,7 +233,7 @@
"नयाँ""कुनै नयाँ एपहरू छैनन्।""ट्याब्लेट विकल्पहरू"
- "Android TV सम्बन्धी विकल्पहरू"
+ "Android टिभी सम्बन्धी विकल्पहरू""फोन विकल्पहरू""स्क्रिन बन्द""बन्द गर्नुहोस्"
@@ -359,7 +357,7 @@
"एपलाई SMS सन्देशहरू पठाउन अनुमति दिन्छ। यसले अप्रत्यासित चार्जहरूको परिणाम दिन सक्दछ। खराब एपहरूले तपाईंको पुष्टि बिना सन्देशहरू पठाएर तपाईंको पैसा खर्च गराउन सक्दछ।""तपाईंका पाठ सन्देशहरू (SMS वा MMS) पढ्नुहोस्""यस अनुप्रयोगले तपाईंको ट्याब्लेटमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"
- "यस अनुप्रयोगले तपाईंको Android TV यन्त्रमा भण्डार गरिएका सबै SMS.(पाठ) सन्देशहरू पढ्न सक्छ।"
+ "यस अनुप्रयोगले तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका सबै SMS.(पाठ) सन्देशहरू पढ्न सक्छ।""यस अनुप्रयोगले तपाईंको फोनमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।""पाठ सन्देशहरू (WAP) प्राप्त गर्नुहोस्""WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका सन्देशहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"
@@ -381,7 +379,7 @@
"यो अनुप्रयोगले पृष्ठभूमिमा डेटा प्रयोग गर्नसक्छ। यसले गर्दा धेरै डेटा प्रयोग हुनसक्छ।""एपहरू जहिले पनि चल्ने बनाउनुहोस्""यसको आफ्नै मेमोरीमा दृढ भएकोको अंश बनाउनको लागि एपलाई अनुमति दिन्छ। ट्याब्लेटलाई ढिलो गराउँदै गरेका अन्य अनुप्रयोगहरूलाई सीमित मात्रामा यसले मेमोरी उपलब्ध गराउन सक्छ।"
- "एपलाई आफ्ना केही अंशहरू मेमोरीमा स्थायी रूपमा राख्ने अनुमति दिन्छ। यसले गर्दा अन्य अनुप्रयोगहरूका लागि मेमोरीको अभाव हुन सक्ने भएकाले तपाईंको Android TV यन्त्र सुस्त हुन सक्छ।"
+ "एपलाई आफ्ना केही अंशहरू मेमोरीमा स्थायी रूपमा राख्ने अनुमति दिन्छ। यसले गर्दा अन्य अनुप्रयोगहरूका लागि मेमोरीको अभाव हुन सक्ने भएकाले तपाईंको Android टिभी यन्त्र सुस्त हुन सक्छ।""एपलाई मेमोरीमा आफैंको निरन्तरको अंश बनाउन अनुमति दिन्छ। यसले फोनलाई ढिला बनाएर अन्य एपहरूमा मेमोरी SIMित गर्न सक्दछन्।""अग्रभूमिको सेवा सञ्चालन गर्नुहोस्""एपलाई अग्रभूमिका सेवाहरू प्रयोग गर्ने अनुमति दिन्छ।"
@@ -391,35 +389,35 @@
"प्रणालीका सेटिङ डेटालाई परिवर्तन गर्नको लागि एपलाई अनुमति दिन्छ। खराब एपहरूले सायद तपाईँको प्रणालीको कन्फिगरेसनलाई क्षति पुर्याउन सक्छन्।""स्टार्टअपमा चलाउनुहोस्""आनुप्रयोगलाई प्रणाली बुट प्रक्रिया पूर्ण हुने बितिकै आफैलाई सुरु गर्ने अनुमति दिन्छ। यसले ट्याब्लेट सुरु गर्नमा ढिला गर्न सक्दछ र एपलाई समग्रमा ट्याब्लेट सधैँ चालु गरेर ढिला बनाउँदछ।"
- "एपलाई प्रणाली बुट हुने बित्तिकै स्वत: सुरु हुने अनुमति दिन्छ। यसो गर्दा एप सधैँ चलिरहने भएकाले तपाईंको Android TV यन्त्र सुरु हुन बढी समय लाग्नुका साथै यन्त्रको समग्र कार्यसम्पादन सुस्त हुन सक्छ।"
+ "एपलाई प्रणाली बुट हुने बित्तिकै स्वत: सुरु हुने अनुमति दिन्छ। यसो गर्दा एप सधैँ चलिरहने भएकाले तपाईंको Android टिभी यन्त्र सुरु हुन बढी समय लाग्नुका साथै यन्त्रको समग्र कार्यसम्पादन सुस्त हुन सक्छ।""एपलाई प्रणाली बुट गरी सकेपछि जति सक्दो चाँडो आफैंमा सुरु गर्न अनुमति दिन्छ। यसले फोन सुरु गर्नमा ढिला गर्न सक्दछ र अनप्रयोगलाई समग्रमा फोन सधैँ चालु गरेर ढिला बनाउँदछ।""स्टिकि प्रसारण पठाउनुहोस्""औपचारिक प्रसारणलाई पठाउनको लागि एउटा एपलाई अनुमति दिन्छ, जुन प्रसारण समाप्त भएपछि बाँकी रहन्छ। अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग गरेको कारणले ट्याब्लेटलाई ढिलो र अस्थिर बनाउन सक्छ।"
- "एपलाई प्रसारण समाप्त भइसकेपछि पनि रहिरहने स्टिकी प्रसारणहरू पठाउने अनुमति दिन्छ। यो सुविधाको अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग हुने भएकाले तपाईंको Android TV यन्त्र सुस्त वा अस्थिर हुन सक्छ।"
+ "एपलाई प्रसारण समाप्त भइसकेपछि पनि रहिरहने स्टिकी प्रसारणहरू पठाउने अनुमति दिन्छ। यो सुविधाको अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग हुने भएकाले तपाईंको Android टिभी यन्त्र सुस्त वा अस्थिर हुन सक्छ।""औपचारिक प्रसारणलाई पठाउनको लागि एक एपलाई अनुमति दिन्छ, जुन प्रसारण समाप्त भएपछि बाँकी रहन्छ। अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग गरेको कारणले फोनलाई ढिलो र अस्थिर बनाउन सक्छ।""तपाईँका सम्पर्कहरू पढ्नुहोस्""एपलाई तपाईंको ट्याब्लेटमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको ट्याब्लेटमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"
- "एपलाई तपाईंको Android TV यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा पढ्न अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको Android TV यन्त्रमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"
+ "एपलाई तपाईंको Android टिभी यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा पढ्न अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।""एपलाई तपाईंको फोनमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको फोनमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।""तपाईँका सम्पर्कहरू परिवर्तन गर्नुहोस्""एपलाई तपाईंको ट्याब्लेटमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"
- "एपलाई तपाईंको Android TV यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"
+ "एपलाई तपाईंको Android टिभी यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।""एपलाई तपाईंको फोनमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।""कल लग पढ्नुहोस्""यस अनुप्रयोगले तपाईंको फोन सम्पर्कको इतिहास पढ्न सक्छ।""कल लग लेख्नुहोस्""आगमन तथा बहर्गमन डेटासहित तपाईँको ट्याब्लेटको कल लगको परिमार्जन गर्न एपलाई अनुमति दिन्छ। खराब एपहरूले यसलाई तपाईँको कल लग परिमार्जन गर्न वा मेटाउन प्रयोग गर्न सक्छन्।"
- "एपलाई तपाईंको Android TV यन्त्रको आगमन र बहिर्गमन कलसम्बन्धी डेटासहित कल लग परिमार्जन गर्ने अनुमति दिन्छ। हानिकारक एपहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्छन्।"
+ "एपलाई तपाईंको Android टिभी यन्त्रको आगमन र बहिर्गमन कलसम्बन्धी डेटासहित कल लग परिमार्जन गर्ने अनुमति दिन्छ। हानिकारक एपहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्छन्।""एपलाई तपाईंको फोनको आउने र बाहिर जाने कलहरूको बारेको डेटा सहित कल लग परिमार्जन गर्न अनुमति दिन्छ। खराब एपहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्दछ।""शरीरका सेन्सरहरूमा पहुँच गराउनुहोस् (जस्तै हृदय धड्कन निगरानीहरू)""तपाईँको हृदय गति जस्तो सेंसर बाट डेटा पहुँचको लागि एप अनुमति दिन्छ जसले तपाईँको भौतिक अवस्था अनुगमन गर्छ।""पात्रोका कार्यक्रम र विवरणहरू पढ्ने""यस अनुप्रयोगले तपाईंको ट्याब्लेटमा भण्डारण गरिएका पात्रो सम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"
- "यस अनुप्रयोगले तपाईंको Android TV यन्त्रमा भण्डार गरिएका पात्रोसम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"
+ "यस अनुप्रयोगले तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका पात्रोसम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।""यस अनुप्रयोगले तपाईंको फोनमा भण्डारण गरिएका पात्रो सम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।""पात्रो घटनाहरू थप्नुहोस् वा परिमार्जन गर्नुहोस् र मालिकको ज्ञान बिना नै पाहुनाहरूलाई इमेल पठाउनुहोस्""यस अनुप्रयोगले तपाईंको ट्याब्लेटमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस अनुप्रयोगले पात्रोका मालिकहरू मार्फत आएको जस्तो लाग्ने सन्देशहरू पठाउन वा तिनीहरूका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"
- "यस अनुप्रयोगले तपाईंको Android TV यन्त्रमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस अनुप्रयोगले पात्रोका मालिकहरूले पठाएको जस्तै देखिने सन्देशहरू पठाउन वा कार्यक्रमका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"
+ "यस अनुप्रयोगले तपाईंको Android टिभी यन्त्रमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस अनुप्रयोगले पात्रोका मालिकहरूले पठाएको जस्तै देखिने सन्देशहरू पठाउन वा कार्यक्रमका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।""यस अनुप्रयोगले तपाईंको फोनमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस अनुप्रयोगले पात्रोका मालिकहरू मार्फत आएको जस्तो लाग्ने सन्देशहरू पठाउन वा तिनीहरूका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।""अधिक स्थान प्रदायक आदेशहरू पहुँच गर्नुहोस्""एपलाई अतिरिक्त स्थान प्रदायक आदेशहरू पहुँच गर्न अनुमति दिन्छ। यो एपलाई GPS वा अन्य स्थान स्रोतहरूको संचालन साथै हस्तक्षेप गर्न अनुमति दिन सक्छ।"
@@ -464,15 +462,15 @@
"उक्त एपलाई यस यन्त्रको फोन नम्बरहरूमाथि पहुँच राख्न दिनुहोस्।""कारको स्क्रिन सक्रिय राख्नुहोस्""ट्याब्लेटलाई निन्द्रामा जानबाट रोक्नुहोस्"
- "आफ्नो Android TV यन्त्रलाई शयन अवस्थामा जान नदिनुहोस्"
+ "आफ्नो Android टिभी यन्त्रलाई शयन अवस्थामा जान नदिनुहोस्""फोनलाई निदाउनबाट रोक्नुहोस्""यो अनुमतिले यस एपलाई कारको स्क्रिन सक्रिय राख्न दिन्छ।""ट्याब्लेटलाई निस्क्रिय हुनबाट रोक्नको लागि एपलाई अनुमति दिन्छ।"
- "एपलाई तपाईंको Android TV यन्त्रलाई शयन अवस्थामा जानबाट रोक्ने अनुमति दिन्छ।"
+ "एपलाई तपाईंको Android टिभी यन्त्रलाई शयन अवस्थामा जानबाट रोक्ने अनुमति दिन्छ।""फोनलाई निस्क्रिय हुनबाट रोक्नको लागि एपलाई अनुमति दिन्छ।""infrared ट्रान्समिट गर्नुहोस्""ट्याबलेटको infrared transmitter प्रयोगको लागि एप अनुमति दिन्छ।"
- "एपलाई तपाईंको Android TV यन्त्रको इन्फ्रारेड ट्रान्समिटर प्रयोग गर्ने अनुमति दिन्छ।"
+ "एपलाई तपाईंको Android टिभी यन्त्रको इन्फ्रारेड ट्रान्समिटर प्रयोग गर्ने अनुमति दिन्छ।""फोनको infrared transmitter प्रयोगको लागि एप अनुमति दिन्छ।""वालपेपर सेट गर्नुहोस्""एपलाई प्रणाली वालपेपर सेट गर्न अनुमति दिन्छ।"
@@ -480,11 +478,11 @@
"प्रणाली वालपेपरको आकार सङ्केतहरू मिलाउन एपलाई अनुमति दिन्छ।""समय क्षेत्र सेट गर्नुहोस्""एपलाई ट्याब्लेटको समय क्षेत्र परिवर्तन गर्न अनुमति दिन्छ।"
- "एपलाई तपाईंको Android TV यन्त्रको समय क्षेत्र परिवर्तन गर्ने अनुमति दिन्छ।"
+ "एपलाई तपाईंको Android टिभी यन्त्रको समय क्षेत्र परिवर्तन गर्ने अनुमति दिन्छ।""एपलाई फोनको समय क्षेत्र परिवर्तन गर्न अनुमति दिन्छ।""उपकरणमा खाताहरू भेट्टाउनुहोस्""एपलाई ट्याब्लेटद्वारा ज्ञात खाताहरूको सूची पाउन अनुमति दिन्छ। यसले अनुप्रयोगद्वारा तपाईंले स्थापित गर्नुभएको कुनै पनि खाताहरू समावेश गर्न सक्दछ।"
- "एपलाई तपाईंको Android TV यन्त्रले चिनेका खाताहरूको सूची प्राप्त गर्ने अनुमति दिन्छ। उक्त सूचीमा तपाईंले स्थापना गर्नुभएका एपहरूले बनाएका कुनै पनि खाताहरू पर्न सक्छन्।"
+ "एपलाई तपाईंको Android टिभी यन्त्रले चिनेका खाताहरूको सूची प्राप्त गर्ने अनुमति दिन्छ। उक्त सूचीमा तपाईंले स्थापना गर्नुभएका एपहरूले बनाएका कुनै पनि खाताहरू पर्न सक्छन्।""फोनलाई थाहा भएका खाताहरूको सूची प्राप्त गर्न एपलाई अनुमति दिन्छ। यसले तपाईँले स्थापना गर्नु भएका अनुप्रयोगहरूबाट सृजित कुनै खाताहरू समावेश हुन सक्छ।""नेटवर्क जडानहरू हेर्नहोस्""एपलाई नेटवर्क जडानहरू जस्तै कुन नेटवर्कहरू अवस्थित हुन्छन् र जडित छन् जसले हेर्नलाई अनुमति दिन्छ।"
@@ -500,21 +498,21 @@
"एपलाई Wi-Fi पहुँच बिन्दुबाट जडान गर्न र विच्छेदन गर्न र Wi-Fi नेटवर्कहरूको लागि उपकरण कन्फिगरेसनमा परिवर्तनहरू गर्न अनुमति दिन्छ।""Wi-Fi Multicast स्विकृतिलाई अनुमति दिनुहोस्""एपलाई मल्टिकाष्ट ठेगानाहरू प्रयोग गरेर Wi-Fi नेटवर्कमा पठाइएको प्याकेटहरू प्राप्त गर्न अनुमति दिन्छ, केवल तपाईंको ट्याब्लेट मात्र होइन। यसले गैर-मल्टिकाष्ट मोड भन्दा बढी उर्जा प्रयोग गर्दछ।"
- "एपलाई मल्टिकास्ट ठेगानाहरू प्रयोग गरी तपाईंको Android TV यन्त्रमा मात्र नभई कुनै Wi-Fi नेटवर्कमा जोडिएका सबै यन्त्रहरूमा पठाइएका प्याकेटहरू प्राप्त गर्ने अनुमति दिन्छ। यसले गैर मल्टिकास्ट मोडभन्दा बढी पावर खपत गर्छ।"
+ "एपलाई मल्टिकास्ट ठेगानाहरू प्रयोग गरी तपाईंको Android टिभी यन्त्रमा मात्र नभई कुनै Wi-Fi नेटवर्कमा जोडिएका सबै यन्त्रहरूमा पठाइएका प्याकेटहरू प्राप्त गर्ने अनुमति दिन्छ। यसले गैर मल्टिकास्ट मोडभन्दा बढी पावर खपत गर्छ।""तपाईँको फोन मात्र होइन, मल्टिकास्ट ठेगानाहरूको प्रयोग गरे Wi-Fi नेटवर्कका सबै उपकरणहरूमा पठाइएका प्याकेटहरू प्राप्त गर्न एपलाई अनुमति दिन्छ। यसले गैर-मल्टिकास्ट मोडभन्दा बढी उर्जा प्रयोग गर्छ।""ब्लुटुथ सेटिङहरूमा पहुँच गर्नुहोस्""स्थानीय ब्लुटुथ ट्याब्लेटलाई कन्फिगर गर्नको लागि र टाढाका उपकरणहरूलाई पत्ता लगाउन र जोड्नको लागि एपलाई अनुमति दिन्छ।"
- "एपलाई तपाईंको Android TV यन्त्रको ब्लुटुथ कन्फिगर गर्ने तथा टाढा रहेका यन्त्रहरू पत्ता लगाई ती यन्त्रहरूसँग जोडा बनाउने अनुमति दिन्छ।"
+ "एपलाई तपाईंको Android टिभी यन्त्रको ब्लुटुथ कन्फिगर गर्ने तथा टाढा रहेका यन्त्रहरू पत्ता लगाई ती यन्त्रहरूसँग जोडा बनाउने अनुमति दिन्छ।""एपलाई स्थानीय ब्लुटुथ फोन कन्फिगर गर्न र टाढाका उपकरणहरूसँग खोज गर्न र जोडी गर्न अनुमति दिन्छ।""WiMAXसँग जोड्नुहोस् वा छुटाउनुहोस्""एपलाई वाइम्याक्स सक्षम छ कि छैन र जडान भएको कुनै पनि वाइम्याक्स नेटवर्कहरूको बारेमा जानकारी निर्धारिण गर्न अनुमति दिन्छ।""वाइम्याक्स अवस्था परिवर्तन गर्नुहोस्""एपलाई वाइम्याक्स नेटवर्कहरूबाट ट्याब्लेट जडान गर्न र ट्याब्लेट विच्छेदन गर्न अनुमति दिन्छ।"
- "एपलाई तपाईंको Android TV यन्त्र WiMAX नेटवर्कहरूमा जोड्ने वा ती नेटवर्कहरूबाट विच्छेद गर्ने अनुमति दिन्छ।"
+ "एपलाई तपाईंको Android टिभी यन्त्र WiMAX नेटवर्कहरूमा जोड्ने वा ती नेटवर्कहरूबाट विच्छेद गर्ने अनुमति दिन्छ।""वाइम्याक्स नेटवर्कहरूसँग फोन जोड्न र छुटाउन एपलाई अनुमति दिन्छ।""ब्लुटुथ उपकरणहरूसँग जोडी मिलाउनुहोस्""ट्याब्लेटमा ब्लुटुथको कन्फिगुरेसनलाई हेर्न र बनाउन र जोडी उपकरणहरूसँग जडानहरूलाई स्वीकार गर्न एपलाई अनुमति दिन्छ।"
- "एपलाई तपाईंको Android TV यन्त्रको ब्लुटुथको कन्फिगुरेसन हेर्ने तथा जोडा बनाइएका यन्त्रहरूसँग जोडिने वा ती यन्त्रहरूले पठाएका जोडिने अनुरोध स्वीकार्ने अनुमति दिन्छ।"
+ "एपलाई तपाईंको Android टिभी यन्त्रको ब्लुटुथको कन्फिगुरेसन हेर्ने तथा जोडा बनाइएका यन्त्रहरूसँग जोडिने वा ती यन्त्रहरूले पठाएका जोडिने अनुरोध स्वीकार्ने अनुमति दिन्छ।""एपलाई फोनमा ब्लुटुथको कन्फिगरेसन हेर्न र जोडी भएका उपकरणहरूसँग जडानहरू बनाउन र स्वीकार गर्न अनुमति दिन्छ।""NFC भुक्तानी सेवासम्बन्धी रुचाइएको जानकारी""यसले एपलाई दर्ता गरिएका सहायता तथा मार्गको गन्तव्य जस्ता रुचाइएका NFC भुक्तानी सेवासम्बन्धी जानकारी प्राप्त गर्न दिन्छ।"
@@ -675,10 +673,10 @@
"स्क्रिन लक पासवर्ड र PIN हरूमा अनुमति दिइएको लम्बाइ र वर्णहरूको नियन्त्रण गर्नुहोस्।""मनिटरको स्क्रिन अनलक गर्ने प्रयासहरू""स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने ट्याब्लेट लक गर्नुहोस् वा ट्याब्लेटका सबै डेटा मेट्नुहोस्।"
- "स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android TV यन्त्र लक गर्नुहोस् वा यन्त्रमा भएको सम्पूर्ण डेटा मेटाउनुहोस्।"
+ "स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा यन्त्रमा भएको सम्पूर्ण डेटा मेटाउनुहोस्।""स्क्रिनअनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने फोन लक गर्नुहोस् वा फोनका सबै डेटा मेट्नुहोस्।""स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा ट्याब्लेट लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।"
- "स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android TV यन्त्र लक गर्नुहोस् वा यो प्रयोगकर्ताको सम्पूर्ण डेटा मेटाउनुहोस्।"
+ "स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा यो प्रयोगकर्ताको सम्पूर्ण डेटा मेटाउनुहोस्।""स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा फोन लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।""स्क्रिन लक परिवर्तन गर्ने""स्क्रिन लक परिवर्तन गर्नुहोस्।"
@@ -686,11 +684,11 @@
"कसरी र कहिले स्क्रिन लक गर्ने नियन्त्रण गर्नुहोस्।""सबै डेटा मेट्नुहोस्""एउटा फ्याक्ट्रि डेटा रिसेट गरेर चेतावनी नआउँदै ट्याबल्टको डेटा मेट्नुहोस्।"
- "फ्याक्ट्री डेटा रिसेट गरेर चेतावनी नदिइकन आफ्नो Android TV यन्त्रको डेटा मेटाउनुहोस्।"
+ "फ्याक्ट्री डेटा रिसेट गरेर चेतावनी नदिइकन आफ्नो Android टिभी यन्त्रको डेटा मेटाउनुहोस्।""एउटा फ्याक्ट्रि डेटा रिसेट गरेर चेतावनी नदिइकन फोनको डेटा मेट्न।""प्रयोगकर्ता डेटा मेट्नुहोस्""चेतावनी बिना यो ट्याब्लेटमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।"
- "यो Android TV यन्त्रमा भएको यस प्रयोगकर्ताको डेटा चेतावनी नदिइकन मेटाउनुहोस्।"
+ "यो Android टिभी यन्त्रमा भएको यस प्रयोगकर्ताको डेटा चेतावनी नदिइकन मेटाउनुहोस्।""चेतावनी बिना यो फोनमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।""उपकरण विश्वव्यापी प्रोक्सी मिलाउनुहोस्""नीति सक्षम हुँदा प्रयोग गरिनको लागि यन्त्र ग्लोवल प्रोक्सी सेट गर्नुहोस्। केवल यन्त्र मालिकले ग्लोवल प्रोक्सी सेट गर्न सक्नुहुन्छ।"
@@ -840,7 +838,7 @@
"अत्यधिक मोहडा खोल्ने प्रयासहरू बढी भए।""SIM कार्ड छैन""ट्याब्लेटमा SIM कार्ड छैन।"
- "तपाईंको Android TV यन्त्रमा SIM कार्ड छैन।"
+ "तपाईंको Android टिभी यन्त्रमा SIM कार्ड छैन।""फोनमा SIM कार्ड छैन।""SIM कार्ड घुसाउनुहोस्""SIM कार्ड छैन वा पढ्न मिल्दैन। SIM कार्ड हाल्नुहोस्।"
@@ -863,13 +861,13 @@
"तपाईंले गलत तरिकाले आफ्नो पासवर्ड %1$d पटक टाइप गर्नुभयो। \n\n%2$d सेकेन्डमा फेरि प्रयास गर्नुहोस्।""तपाईँले गलत तरिकाले तपाईँको PIN %1$d पटक टाइप गर्नु भएको छ। \n\n%2$d सेकेन्डमा फेरि प्रयास गर्नुहोस्।""तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले %1$d पटक खिच्नु भएको छ। पछि %2$d थप असफल कोसिसहरू, तपाईँको Google साइन इन प्रयोग गरी तपाईँको ट्याब्लेट अनलक गर्न भनिने छ।\n\n %3$d सेकेन्डमा फरि प्रयास गर्नुहोस्।"
- "तपाईंले आफ्नो अनलक शैली %1$d पटक गलत तरिकाले कोर्नुभएको छ। थप %2$d प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो Google खाता मार्फत साइन इन गरेर आफ्नो Android TV यन्त्र अनलक गर्न अनुरोध गरिनेछ।\n\n %3$d सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"
+ "तपाईंले आफ्नो अनलक शैली %1$d पटक गलत तरिकाले कोर्नुभएको छ। थप %2$d प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो Google खाता मार्फत साइन इन गरेर आफ्नो Android टिभी यन्त्र अनलक गर्न अनुरोध गरिनेछ।\n\n %3$d सेकेन्डपछि फेरि प्रयास गर्नुहोस्।""तपाईँले %1$d पटक गलत तरिकाले तपाईँको अनलक ढाँचालाई कोर्नु भएको छ। पछि %2$d अरू धेरै असफल कोसिसहरूपछि, तपाईँलाई तपाईँको फोन Google साइन इन प्रयोग गरेर अनलक गर्नको लागि सोधिने छ। \n\n %3$d सेकेन्डमा पुनः प्रयास गर्नुहोस्।""तपाईँले %1$d पटक ट्याब्लेटलाई अनलक गर्नको लागि गलत तरिकाले कोशिस गर्नुभएको छ। %2$d अरू धेरै असफल कोसिसहरूपछि, ट्याब्लेट फ्याट्रि पूर्वनिर्धारितमा रिसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"
- "तपाईंले आफ्नो Android TV यन्त्र %1$d पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप %2$d प्रयासहरू असफल भएपछि तपाईंको Android TV यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"
+ "तपाईंले आफ्नो Android टिभी यन्त्र %1$d पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप %2$d प्रयासहरू असफल भएपछि तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।""तपाईंले गलत तरिकाले %1$d पटक फोन अनलक गर्ने प्रयत्न गर्नुभयो। %2$d बढी असफल प्रयत्नहरू पछि, फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।""तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले %d पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ।"
- "तपाईंले आफ्नो Android TV यन्त्र %d पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android TV यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिनेछ।"
+ "तपाईंले आफ्नो Android टिभी यन्त्र %d पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिनेछ।""तपाईंले गलत तरिकाले फोन %d पटक अनलक गर्ने प्रयत्न गर्नुभयो। अब फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ।""%d सेकेन्डमा फेरि प्रयास गर्नुहोस्।""ढाँचा बिर्सनु भयो?"
@@ -956,7 +954,7 @@
"ब्राउजरले भ्रमण गरेको सबै URL हरूको इतिहास र ब्राउजरका सबै बुकमार्कहरू पढ्नको लागि एपलाई अनुमति दिन्छ। नोट: यो अनुमतिलाई तेस्रो पक्ष ब्राउजरहरूद्वारा वा वेब ब्राउज गर्ने क्षमताद्वारा बलपूर्वक गराउन सकिँदैन।""वेब बुकमार्कहरू र इतिहास लेख्नुहोस्""एपलाई तपाईंको ट्याब्लेटमा भण्डार गरिएको ब्राउजरको इतिहास वा बुकमार्कहरू परिमार्जन गर्न अनुमति दिन्छ। यसले एपलाई ब्राजर डेटा मेटाउन वा परिमार्जन गर्न अनुमति दिन सक्दछ। टिप्पणी: यो अनुमति वेब ब्राउज गर्ने क्षमताहरूको साथ तेस्रो-पार्टी ब्राउजर वा अन्य अनुप्रयोगहरूद्वारा लागू गरिएको होइन।"
- "एपलाई तपाईंको Android TV यन्त्रमा भण्डार गरिएका ब्राउजरको इतिहास र पुस्तक चिन्हहरू परिमार्जन गर्ने अनुमति दिन्छ। यसले एपलाई ब्राउजरको डेटा मेटाउने वा परिमार्जन गर्ने अनुमति दिन सक्छ। ध्यान दिनुहोस्: तेस्रो पक्षीय ब्राउजर वा वेब ब्राउज गर्ने सुविधा प्रदान गर्ने अन्य एपहरूले यो अनुमति लागू गर्न सक्दैनन्।"
+ "एपलाई तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका ब्राउजरको इतिहास र पुस्तक चिन्हहरू परिमार्जन गर्ने अनुमति दिन्छ। यसले एपलाई ब्राउजरको डेटा मेटाउने वा परिमार्जन गर्ने अनुमति दिन सक्छ। ध्यान दिनुहोस्: तेस्रो पक्षीय ब्राउजर वा वेब ब्राउज गर्ने सुविधा प्रदान गर्ने अन्य एपहरूले यो अनुमति लागू गर्न सक्दैनन्।""तपाईँको फोनमा भण्डारण भएको ब्राउजरको इतिहास वा बुकमार्कहरू परिवर्तन गर्नको लागि एपलाई अनुमति दिन्छ। यसले सायद ब्राउजर डेटालाई मेट्न वा परिवर्तन गर्नको लागि एपलाई अनुमति दिन्छ। नोट: वेब ब्राउज गर्ने क्षमतासहितका अन्य एपहरू वा तेस्रो- पक्ष ब्राउजरद्वारा सायद यस अनुमतिलाई लागु गर्न सकिंदैन।""एउटा आलर्म सेट गर्नुहोस्""स्थापना गरिएको सङ्केत घडी अनुप्रयोगमा सङ्केत समय मिलाउन एपलाई अनुमति दिन्छ। केही सङ्केत घडी एपहरूले यो सुविधा कार्यान्वयन नगर्न सक्छन्।"
@@ -1125,14 +1123,10 @@
"जाँच गरिएको""जाँच गरिएको छैन""प्रयोग गरेर कारबाही पुरा गर्नुहोस्"
-
-
-
+ "निम्न एपको प्रयोग गरी कारबाही पुरा गर्नुहोस्: %1$s""पूर्ण कारबाही""निम्नबाट खोल्नुहोस्"
-
-
-
+ "निम्न एपमा खोल्नुहोस्: %1$s""खोल्नुहोस्""निम्नमार्फत %1$s का लिंकहरू खोल्नुहोस्""निम्नमार्फत लिंकहरू खोल्नुहोस्"
@@ -1557,7 +1551,7 @@
"एक पटक मात्र""%1$s कार्य प्रोफाइल समर्थन गर्दैन""ट्याब्लेट"
- "TV"
+ "टिभी""फोन""डक स्पिकरहरू""HDMI"
@@ -1615,22 +1609,21 @@
"तपाईँले तपाईँक पासवर्ड %1$d पटक गलत टाइप गर्नुभएको छ। \n\n %2$d सेकेन्डमा फेरि प्रयास गर्नुहोस्।""तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले %1$d पटक खिच्नु भएको छ। \n\n %2$d सेकेन्डमा फेरि कोसिस गर्नुहोस्।""तपाईँले ट्याब्लेटलाई अनलक गर्न गलत तरिकाले %1$d पटक कोसिस गर्नु भएको छ। %2$d पछि थप असफल प्रयासहरू, ट्याब्लेट पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"
- "तपाईंले आफ्नो Android TV यन्त्र %1$d पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप %2$d प्रयासहरू असफल भएपछि तपाईंको Android TV यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"
+ "तपाईंले आफ्नो Android टिभी यन्त्र %1$d पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप %2$d प्रयासहरू असफल भएपछि तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।""तपाईँले गलतसँग फोनलाई अनलक गर्न %1$d पटक कोसिस गर्नु भयो। %2$d पछि थप असफल कोसिसहरू, फोनलाई पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।""तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले %d पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ।"
- "तपाईंले आफ्नो Android TV यन्त्र %d पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android TV यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिनेछ।"
+ "तपाईंले आफ्नो Android टिभी यन्त्र %d पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिनेछ।""तपाईंले गलत तरिकाले फोन %d पटक अनलक गर्ने प्रयत्न गर्नुभयो। अब फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ।""तपाईंले गलत तरिकाले आफ्नो अनलक ढाँचा %1$d पटक कोर्नुभयो। %2$d विफल प्रयत्नहरू पछि, तपाईंलाई आफ्नो ट्याब्लेट इमेल खाता प्रयोग गरेर अनलक गर्न सोधिने छ।\n\n फेरि प्रयास गर्नुहोस् %3$d सेकेन्डहरूमा।"
- "तपाईंले आफ्नो अनलक शैली %1$d पटक गलत तरिकाले कोर्नुभएको छ। थप %2$d प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो इमेल खाता प्रयोग गरेर आफ्नो Android TV यन्त्र अनलक गर्न अनुरोध गरिनेछ।\n\n %3$d सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"
+ "तपाईंले आफ्नो अनलक शैली %1$d पटक गलत तरिकाले कोर्नुभएको छ। थप %2$d प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो इमेल खाता प्रयोग गरेर आफ्नो Android टिभी यन्त्र अनलक गर्न अनुरोध गरिनेछ।\n\n %3$d सेकेन्डपछि फेरि प्रयास गर्नुहोस्।""तपाईँले आफ्नो अनलक ढाँचा गलत रूपमा %1$d पटक तान्नु भएको छ। %2$d धेरै असफल प्रयासहरूपछि, तपाईँलाई एउटा इमेल खाताको प्रयोग गरेर तपाईँको फोन अनलक गर्न सोधिने छ।\n\n फेरि %3$d सेकेन्डमा प्रयास गर्नुहोस्।"" — ""हटाउनुहोस्"
- "%1$s को पृष्ठभूमिबाट सुरु गरिने अग्रभूमि सेवाका भविष्यमा आउने R बिल्डहरूमा चलाउँदै गर्दा प्रयोग गर्ने अनुमतिको दिइने छैन। कृपया go/r-bg-fgs-restriction हेर्नुहोस् र कुनै बगसम्बन्धी रिपोर्ट फाइल गर्नुहोस्।""सिफारिस तहभन्दा आवाज ठुलो गर्नुहुन्छ?\n\nलामो समय सम्म उच्च आवाजमा सुन्दा तपाईँको सुन्ने शक्तिलाई हानी गर्न सक्छ।""पहुँच सम्बन्धी सर्टकट प्रयोग गर्ने हो?""यो सर्टकट सक्रिय हुँदा, ३ सेकेन्डसम्म दुवै भोल्युम बटन थिच्नुले पहुँचसम्बन्धी कुनै सुविधा सुरु गर्ने छ।""पहुँचसम्बन्धी सुविधाहरू सक्रिय गर्ने हो?"
- "केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुले पहुँचसम्बन्धी सुविधाहरू सक्रिय गर्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nहालका सुविधाहरू:\n%1$s\nतपाईं सेटिङ > पहुँचमा गएर चयन गरिएका सुविधाहरू परिवर्तन गर्न सक्नुहुन्छ।"
+ "केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुभयो भने पहुँचसम्बन्धी सुविधाहरू सक्रिय हुन्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nहालका सुविधाहरू:\n%1$s\nतपाईं सेटिङ > पहुँचमा गएर चयन गरिएका सुविधाहरू परिवर्तन गर्न सक्नुहुन्छ।"" • %1$s\n""%1$s सक्रिय गर्ने हो?""केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुले %1$s नामक पहुँचसम्बन्धी सुविधा सक्रिय गर्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nतपाईं सेटिङ > पहुँचमा गई यो सर्टकटमार्फत अर्को सुविधा खुल्ने बनाउन सक्नुहुन्छ।"
@@ -1657,13 +1650,13 @@
"सर्टकट प्रयोग गर्नुहोस्""रङ्ग उल्टाउने सुविधा""रङ्ग सच्याउने सुविधा"
- "तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। %1$s सक्रिय पारियो।"
- "तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। %1$s निष्क्रिय पारियो।"
+ "तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। %1$s अन भयो।"
+ "तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। %1$s अफ भयो।""%1$s प्रयोग गर्न दुवै भोल्युम कुञ्जीहरूलाई तीन सेकेन्डसम्म थिचिराख्नुहोस्""तपाईंले पहुँचको बटन ट्याप गर्दा प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस्:""तपाईंले पहुँचको इसारामार्फत प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस् (दुईवटा औँलाले स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्):""तपाईंले पहुँचको इसारामार्फत प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस् (तीनवटा औँलाले स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्):"
- "एउटा सुविधाबाट अर्को सुविधामा जान पहुँचको बटनमा छोइराख्नुहोस्।"
+ "एउटा सुविधाबाट अर्को सुविधामा जान पहुँच बटन छोइराख्नुहोस्।""एउटा सुविधाबाट अर्को सुविधामा जान दुईवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्।""एउटा सुविधाबाट अर्को सुविधामा जान तीनवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्।""म्याग्निफिकेसन"
@@ -1950,12 +1943,12 @@
"%1$s"" मा अद्यावधिक गर्ने हो?""%1$s लाई ""%2$s"" मा अद्यावधिक गर्ने हो?""%1$s र %2$s लाई ""%3$s"" मा अद्यावधिक गर्ने हो?"
- "यी वस्तुहरू ""%4$s"" मा अद्यावधिक गर्नुहोस्: %1$s, %2$s र %3$s हो?"
+ "यी वस्तुहरू ""%4$s"" मा अपडेट गर्नुहोस्: %1$s, %2$s र %3$s हो?""सुरक्षित गर्नुहोस्""पर्दैन, धन्यवाद""अहिले होइन""कहिल्यै होइन"
- "अद्यावधिक गर्नुहोस्"
+ "अपडेट गर्नुहोस्""जारी राख्नुहोस्""पासवर्ड""ठेगाना"
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 98fece3b3c6c20dce0b65d637eae188be39e7aa6..39cdce9cc46babd559bfdd510247e722dcae1d95 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -35,6 +35,7 @@
#FFFFFF#FFFFFF
+ #8AB4F8#8AB4F8
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index cc8137cc244c45f65e56808762a982e830d0950e..faeb721f7c1f7957141bf20c3cdede02277e475a 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -202,10 +202,8 @@
"Afdrukken uitgeschakeld door %s.""Schakel je werkprofiel in""Je persoonlijke apps zijn geblokkeerd totdat je je werkprofiel inschakelt"
-
-
-
-
+ "Apps die worden gebruikt voor persoonlijke doeleinden, worden geblokkeerd op %1$s om %2$s. Je IT-beheerder staat niet toe dat je werkprofiel langer dan %3$d dagen is uitgeschakeld."
+ "Inschakelen""Ik""Tabletopties""Opties voor Android TV"
@@ -239,7 +237,7 @@
"Telefoonopties""Schermvergrendeling""Uitschakelen"
- "Voeding"
+ "Aan/uit""Opnieuw opstarten""Noodgeval""Bugrapport"
@@ -953,11 +951,11 @@
"Gebied""Emiraat""je webbladwijzers en -geschiedenis lezen"
- "Hiermee kan de app de geschiedenis lezen van alle URL\'s die in de systeemeigen browser zijn bezocht, en alle bladwijzers in de systeemeigen browser. Let op: deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."
+ "Hiermee kan de app de geschiedenis lezen van alle URL\'s die in de systeemeigen browser zijn bezocht, en alle bookmarks in de systeemeigen browser. Let op: deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden.""webbladwijzers en -geschiedenis schrijven"
- "Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bladwijzers die zijn opgeslagen op je tablet. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden.."
- "Hiermee kan de app de browsergeschiedenis of opgeslagen bladwijzers bewerken op je Android TV-apparaat. Hierdoor kan de app mogelijk browsergegevens wissen of aanpassen. Opmerking: Dit recht kan niet worden afgedwongen door andere browsers of andere apps met internetmogelijkheden."
- "Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bladwijzers die zijn opgeslagen op je telefoon. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden."
+ "Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bookmarks die zijn opgeslagen op je tablet. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden.."
+ "Hiermee kan de app de browsergeschiedenis of opgeslagen bookmarks bewerken op je Android TV-apparaat. Hierdoor kan de app mogelijk browsergegevens wissen of aanpassen. Opmerking: Dit recht kan niet worden afgedwongen door andere browsers of andere apps met internetmogelijkheden."
+ "Hiermee kan de app de webgeschiedenis wijzigen in de systeemeigen browser en de bookmarks die zijn opgeslagen op je telefoon. Deze toestemming kan niet worden geforceerd door andere browsers of andere apps met internetmogelijkheden.""een wekker instellen""Hiermee kan de app een wekker instellen in een geïnstalleerde wekker-app. Deze functie wordt door sommige wekker-apps niet geïmplementeerd.""voicemail toevoegen"
@@ -1550,7 +1548,7 @@
"Browser starten?""Gesprek accepteren?""Altijd"
- "Één keer"
+ "Eén keer""%1$s ondersteunt werkprofielen niet""Tablet""Tv"
@@ -1621,7 +1619,6 @@
"Je hebt je ontgrendelingspatroon %1$d keer onjuist getekend. Na nog eens %2$d mislukte pogingen wordt u gevraagd je telefoon te ontgrendelen via een e-mailaccount.\n\n Probeer het over %3$d seconden opnieuw."" — ""Verwijderen"
- "De op de achtergrond gestarte voorgrondservice van %1$s heeft geen rechten tijdens gebruik in toekomstige R-builds. Ga naar go/r-bg-fgs-restriction en dien een bugrapport in.""Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd.""Snelkoppeling toegankelijkheid gebruiken?""Als de snelkoppeling is ingeschakeld, kun je drie seconden op beide volumeknoppen drukken om een toegankelijkheidsfunctie te starten."
@@ -1789,7 +1786,7 @@
"2e %1$s, werk""3e %1$s, werk""Vraag pin voor losmaken"
- "Vraag patroon voor losmaken"
+ "Vraag om ontgrendelingspatroon voor losmaken""Vraag wachtwoord voor losmaken""Geïnstalleerd door je beheerder""Geüpdatet door je beheerder"
@@ -1912,7 +1909,7 @@
"Demo starten…""Apparaat resetten…""%1$s uitgeschakeld"
- "Telefonische vergadering"
+ "Conferencecall""Knopinfo""Games""Muziek en audio"
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index f803f02e92bc5d8b52e0ff87b966b04bb9fd2ffa..873cdbb89c008af9238343aab5d50c7b2413a7bd 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -60,7 +60,7 @@
"ଆଉଟଗୋଇଙ୍ଗ୍ କଲର୍ ଆଇଡି""ସଂଯୁକ୍ତ ଲାଇନ୍ ID""ସଂଯୁକ୍ତ ଲାଇନ୍ ID କଟକଣା"
- "କଲ୍ ଫରୱାର୍ଡିଙ୍ଗ"
+ "କଲ୍ ଫରୱାର୍ଡିଂ""କଲ୍ ଅପେକ୍ଷାରତ""କଲ୍ ବ୍ୟାରିଙ୍ଗ୍""ପାସ୍ୱର୍ଡ ପରିବର୍ତ୍ତନ"
@@ -88,7 +88,7 @@
"ଜରୁରୀକାଳୀନ କଲ୍ ଉପଲବ୍ଧ ନାହିଁ""ୱାଇ-ଫାଇ ସାହାଯ୍ୟରେ ଜରୁରୀକାଳୀନ କଲ୍ କରାଯାଇପାରିବ ନାହିଁ""ଆଲର୍ଟ"
- "କଲ୍ ଫରୱାର୍ଡିଙ୍ଗ"
+ "କଲ୍ ଫରୱାର୍ଡିଂ""ଜରୁରୀକାଳୀନ କଲବ୍ୟାକ୍ ମୋଡ୍""ମୋବାଇଲ୍ ଡାଟା ଷ୍ଟାଟସ୍""SMS ମେସେଜ୍"
@@ -175,7 +175,7 @@
"ବହୁତ %sକୁ ଡିଲିଟ୍ କରିବା ପାଇଁ ଚେଷ୍ଟା କରାଯାଇଛି""ଟାବଲେଟ୍ ଷ୍ଟୋରେଜ୍ ପୂର୍ଣ୍ଣ ହୋଇଯାଇଛି। ସ୍ଥାନ ଖାଲି କରିବା ପାଇଁ କିଛି ଫାଇଲ୍ ଡିଲିଟ୍ କରନ୍ତୁ।""ୱାଚ୍ ଷ୍ଟୋରେଜ୍ ପୂର୍ଣ୍ଣ ହୋଇଯାଇଛି। ସ୍ଥାନ ଖାଲି କରିବାକୁ କିଛି ଫାଇଲ୍ ଡିଲିଟ୍ କରନ୍ତୁ।"
- "Android ଟିଭି ଡିଭାଇସ୍ର ଷ୍ଟୋରେଜ୍ ପୂର୍ଣ୍ଣ ଅଛି। ଜାଗା ଖାଲି କରିବାକୁ କିଛି ଫାଇଲ୍ ଡିଲିଟ୍ କରନ୍ତୁ।"
+ "Android TV ଡିଭାଇସ୍ର ଷ୍ଟୋରେଜ୍ ପୂର୍ଣ୍ଣ ଅଛି। ଜାଗା ଖାଲି କରିବାକୁ କିଛି ଫାଇଲ୍ ଡିଲିଟ୍ କରନ୍ତୁ।""ଫୋନ୍ ଷ୍ଟୋରେଜ୍ ପୂର୍ଣ୍ଣ ହୋଇଯାଇଛି। ସ୍ଥାନ ଖାଲି କରିବା ପାଇଁ କିଛି ଫାଇଲ୍ ଡିଲିଟ୍ କରନ୍ତୁ।"ସର୍ଟିଫିକେଟ୍ ଅଥରିଟିଗୁଡ଼ିକ ଇନଷ୍ଟଲ୍ ହେଲା
@@ -202,13 +202,11 @@
"%s ଦ୍ଵାରା ପ୍ରିଣ୍ଟିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି""ୱାର୍କ ପ୍ରୋଫାଇଲ୍ ଚାଲୁ କରନ୍ତୁ""ଆପଣ ଆପଣଙ୍କ ୱାର୍କ ପ୍ରୋଫାଇଲ୍ ଚାଲୁ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ଆପଣଙ୍କର ବ୍ୟକ୍ତିଗତ ଆପ୍ସ ବ୍ଲକ୍ କରାଯାଇଛି"
-
-
-
-
+ "%1$s%2$sରେ ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଆପ୍ସକୁ ବ୍ଲକ୍ କରାଯିବ। ଆପଣଙ୍କ IT ଆଡମିନ୍ ଆପଣଙ୍କ ୱାର୍କ ପ୍ରୋଫାଇଲକୁ %3$dରୁ ଅଧିକ ଦିନ ପାଇଁ ବନ୍ଦ କରିବାକୁ ଅନୁମତି ଦିଅନ୍ତି ନାହିଁ।"
+ "ଚାଲୁ କରନ୍ତୁ""ମୁଁ""ଟାବଲେଟ୍ର ବିକଳ୍ପ"
- "Android ଟିଭିର ବିକଳ୍ପଗୁଡ଼ିକ"
+ "Android TVର ବିକଳ୍ପଗୁଡ଼ିକ""ଫୋନ୍ ବିକଳ୍ପ""ସାଇଲେଣ୍ଟ ମୋଡ୍""ୱେୟାରଲେସ୍କୁ ଚାଲୁ କରନ୍ତୁ"
@@ -226,7 +224,7 @@
"ରିଷ୍ଟାର୍ଟ କରାଯାଉଛି…""ବନ୍ଦ କରାଯାଉଛି…""ଆପଣଙ୍କ ଟାବଲେଟ୍ ବନ୍ଦ ହୋଇଯିବ।"
- "ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ ବନ୍ଦ ହୋଇଯିବ।"
+ "ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ ବନ୍ଦ ହୋଇଯିବ।""ଆପଣଙ୍କ ଘଣ୍ଟା ବନ୍ଦ ହୋଇଯିବ।""ଆପଣଙ୍କ ଫୋନ୍ ବନ୍ଦ ହୋଇଯିବ।""ଆପଣ ବନ୍ଦ କରିବାକୁ ଚାହାନ୍ତି?"
@@ -235,7 +233,7 @@
"ବର୍ତ୍ତମାନର""କୌଣସି ସମ୍ପ୍ରତି ଆପ୍ ନାହିଁ।""ଟାବଲେଟ ବିକଳ୍ପ"
- "Android ଟିଭିର ବିକଳ୍ପଗୁଡ଼ିକ"
+ "Android TVର ବିକଳ୍ପଗୁଡ଼ିକ""ଫୋନ ବିକଳ୍ପ""ସ୍କ୍ରୀନ୍ ଲକ୍""ପାୱାର୍ ବନ୍ଦ"
@@ -359,7 +357,7 @@
"ଆପ୍କୁ SMS ମେସେଜ୍ ପଠେଇବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହାଦ୍ୱାରା ଅପ୍ରତ୍ୟାଶିତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ। ହାନୀକାରକ ଆପ୍ ଆପଣଙ୍କ ବିନା ସ୍ୱୀକୃତିରେ ମେସେଜ୍ ପଠାଇ, ଆପଣଙ୍କ ପଇସା ଖର୍ଚ୍ଚ କରାଇପାରେ।""ଆପଣଙ୍କ ଟେକ୍ସଟ୍ ମେସେଜ୍ (SMS କିମ୍ବା MMS) ପଢ଼ନ୍ତୁ""ଆପଣଙ୍କ ଟାବଲେଟ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ସମସ୍ତ SMS (ଟେକ୍ସଟ୍) ମେସେଜ୍ ଏହି ଆପ୍ ପଢ଼ିପାରେ।"
- "ଏହି ଆପ୍ ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ସମସ୍ତ SMS (ଟେକ୍ସଟ୍) ପଢ଼ି ପାରିବ।"
+ "ଏହି ଆପ୍ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ସମସ୍ତ SMS (ଟେକ୍ସଟ୍) ପଢ଼ି ପାରିବ।""ଆପଣଙ୍କ ଫୋନ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ସମସ୍ତ SMS (ଟେକ୍ସଟ୍) ମେସେଜ୍ ଏହି ଆପ୍ ପଢ଼ିପାରେ।""ଟେକ୍ସଟ୍ ମେସେଜ୍ (WAP) ପ୍ରାପ୍ତ କରନ୍ତୁ""ଆପ୍କୁ WAP ମେସେଜିଙ୍ଗକୁ ପ୍ରାପ୍ତ ଓ ବିକାଶ କରିବାକୁ ଦେଇଥାଏ। ଏହି ଅନୁମତିରେ ଆପଣ ସେମାନଙ୍କୁ ଦେଖାଯାଇଥିବା ମେସେଜ୍ ଉପରେ ନଜର ରଖିବା ଏବଂ ଡିଲିଟ୍ କରିବାର କ୍ଷମତା ସାମିଲ୍ ଅଛି।"
@@ -381,7 +379,7 @@
"ଏହି ଆପ୍ ବ୍ୟାକ୍ଗ୍ରାଉଣ୍ଡରେ ଡାଟା ବ୍ୟବହାର କରିପାରିବ। ଏହା ଦ୍ୱାରା ଅଧିକ ବ୍ୟାଟେରୀ ହୋଇପାରେ।""ଆପ୍କୁ, ସର୍ବଦା ଚାଲୁଥିବା କରନ୍ତୁ""ଆପ୍ଟି ନିଜକୁ ମେମୋରୀରେ ଭାଗ କରିବାକୁ ଦେଇଥାଏ। ଏହାଦ୍ୱାରା ଅନ୍ୟ ଆପ୍ଗୁଡ଼ିକ ପାଇଁ ମେମୋରୀ ଉପଲବ୍ଧକୁ କମ୍ କରିବା ସହ ଟାବ୍ଲେଟ୍ଟିକୁ ମନ୍ଥର କରିବ।"
- "ମେମୋରୀରେ ଅବିରତ ଆପ୍ ନିଜକୁ ନିଜେ ଭାଗ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହା ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଧୀର କରି ଅନ୍ୟ ଆପ୍ ପାଇଁ ଉପଲବ୍ଧ ମେମୋରୀକୁ ସୀମିତ କରିପାରେ।"
+ "ମେମୋରୀରେ ଅବିରତ ଆପ୍ ନିଜକୁ ନିଜେ ଭାଗ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହା ଅନ୍ୟ ଆପ୍ସ ପାଇଁ ଉପଲବ୍ଧ ମେମୋରୀକୁ ସୀମିତ କରି ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଧୀର କରିପାରେ।""ଆପ୍ଟି ନିଜକୁ ମେମୋରୀରେ ଭାଗ କରିବାକୁ ଦେଇଥାଏ। ଏହାଦ୍ୱାରା ଅନ୍ୟ ଆପ୍ଗୁଡ଼ିକ ପାଇଁ ମେମୋରୀ ଉପଲବ୍ଧକୁ କମ୍ କରିବା ସହ ଫୋନ୍ଟିକୁ ମନ୍ଥର କରିବ।""ଫୋର୍ଗ୍ରାଉଣ୍ଡ ସେବାକୁ ଚଲାନ୍ତୁ""ଫୋର୍ଗ୍ରାଉଣ୍ଡ ସେବାଗୁଡ଼ିକୁ ବ୍ୟବହାର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଅନ୍ତୁ।"
@@ -391,11 +389,11 @@
"ଆପ୍କୁ, ସିଷ୍ଟମର ସେଟିଙ୍ଗ ଡାଟା ବଦଳାଇବାକୁ ଦେଇଥାଏ। ହାନୀକାରକ ଆପ୍ ଦ୍ୱାରା ଆପଣଙ୍କ ସିଷ୍ଟମର କନଫିଗରେସନ୍ ଖରାପ ହୋଇପାରେ।""ଆରମ୍ଭ ହେଲେ ଚଲାନ୍ତୁ""ସିଷ୍ଟମ୍ ବୁଟ୍ ଶେଷ ହେବା କ୍ଷଣି ଆପ୍ଟିକୁ ସ୍ୱତଃ ଆରମ୍ଭ ହେବାକୁ ଦେଇଥାଏ। ଏହା କାରଣରୁ ଟାବଲେଟଟ୍ଟି ଚାଲୁ ହେବାରେ ଅଧିକ ସମୟ ଲାଗିପାରେ ଏବଂ ଆପ୍ଟି ଲଗାତାର ଚାଲିବା ଦ୍ୱାରା ସମଗ୍ର ଟାବଲେଟଟ୍ ମନ୍ଥର ହୋଇଯାଇପାରେ।"
- "ଯେତେ ଶୀଘ୍ର ସମ୍ଭବ ସିଷ୍ଟମ୍ ବୁଟିଂ ସମାପ୍ତ ହେବା ପରେ ଆପ୍ ନିଜକୁ ନିଜେ ଆରମ୍ଭ ହେବାକୁ ଅନୁମତି ଦିଏ। ଏହା Android ଟିଭି ଡିଭାଇସ୍ ଆରମ୍ଭ କରିବାକୁ ଅଧିକ ସମୟ ନେଇଥାଏ ଏବଂ ସର୍ବଦା ଚାଲି ସମଗ୍ର ଡିଭାଇସ୍କୁ ଧୀର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
+ "ଯେତେ ଶୀଘ୍ର ସମ୍ଭବ ସିଷ୍ଟମ୍ ବୁଟିଂ ସମାପ୍ତ ହେବା ପରେ ଆପ୍ ନିଜକୁ ନିଜେ ଆରମ୍ଭ ହେବାକୁ ଅନୁମତି ଦିଏ। ଏହା Android TV ଡିଭାଇସ୍ ଆରମ୍ଭ କରିବାକୁ ଅଧିକ ସମୟ ନେବାକୁ ଦେଇଥାଏ ଏବଂ ସର୍ବଦା ଚାଲି ସମଗ୍ର ଡିଭାଇସ୍କୁ ଧୀର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।""ସିଷ୍ଟମ୍ ବୁଟ୍ ଶେଷ ହେବା କ୍ଷଣି ଆପ୍ଟିକୁ ସ୍ୱତଃ ଆରମ୍ଭ ହେବାକୁ ଦେଇଥାଏ। ଏହା କାରଣରୁ ଫୋନ୍ଟି ଚାଲୁ ହେବାରେ ଅଧିକ ସମୟ ଲାଗିପାରେ ଏବଂ ଆପ୍ଟି ଲଗାତାର ଚାଲିବା ଦ୍ୱାରା ସମଗ୍ର ଫୋନ୍ ମନ୍ଥର ହୋଇଯାଇପାରେ।""ଷ୍ଟିକୀ ବ୍ରୋଡକାଷ୍ଟ ପଠାନ୍ତୁ""ଆପ୍କୁ ଷ୍ଟିକୀ ବ୍ରଡ୍କାଷ୍ଟ ପଠାଇବାକୁ ଅନୁମତି ଦେଇଥାଏ, ଯାହା ବ୍ରଡ୍କାଷ୍ଟ ସମାପ୍ତ ହେବାପରେ ବି ରହିଥାଏ। ଅତ୍ୟଧିକ ଉପଯୋଗ ଦ୍ୱାରା ଟାବଲେଟ୍ ମନ୍ଥର ହୋଇପାରେ କିମ୍ବା ଅଧିକ ମେମୋରୀର ବ୍ୟବହାର କରିବା କାରଣରୁ ଏହା ଅସ୍ଥିର ହୋଇପାରେ।"
- "ଷ୍ଟିକି ବ୍ରଡକାଷ୍ଟ୍ ପଠାଇବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ, ଯାହା ବ୍ରଡକାଷ୍ଟ୍ ଶେଷ ହେବାପରେ ରହିଥାଏ। ଅତ୍ୟଧିକ ବ୍ୟବହାର ଦ୍ୱାରା ଅଧିକ ମେମୋରୀ ବ୍ୟବହାର ହୋଇ ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଧୀର କିମ୍ବା ଅସ୍ଥିର କରିପାରେ।"
+ "ଷ୍ଟିକି ବ୍ରଡକାଷ୍ଟ୍ ପଠାଇବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ, ଯାହା ବ୍ରଡକାଷ୍ଟ୍ ଶେଷ ହେବାପରେ ରହିଥାଏ। ଅତ୍ୟଧିକ ବ୍ୟବହାର ଦ୍ୱାରା ଅଧିକ ମେମୋରୀ ବ୍ୟବହାର ହୋଇ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଧୀର କିମ୍ବା ଅସ୍ଥିର କରିପାରେ।""ଷ୍ଟିକୀ ବ୍ରଡ୍କାଷ୍ଟ ପଠାଇବାକୁ ଆପ୍କୁ ଅନୁମତି ଦିଏ, ଯାହା ବ୍ରଡ୍କାଷ୍ଟ ଶେଷ ହେବାପରେ ରହିଥାଏ। ଅତିରିକ୍ତ ବ୍ୟବହାର ଦ୍ୱାରା ଅଧିକ ମେମୋରୀ ବ୍ୟବହାର ହୋଇ ଫୋନ୍କୁ ମନ୍ଥର କିମ୍ବା ଅସ୍ଥିର କରିପାରେ।""ଆପଣଙ୍କ ଯୋଗାଯୋଗ ପଢ଼ନ୍ତୁ""ଏହା ଆପଣଙ୍କ ଟାବ୍ଲେଟ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ଯୋଗାଯୋଗଗୁଡ଼ିକ ବିଷୟରେ ଡାଟା ପଢ଼ିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। ଆପଣଙ୍କର ଟାବ୍ଲେଟ୍ରେ ଥିବା ଆକାଉଣ୍ଟଗୁଡ଼ିକ ଯେଉଁଥିରେ ଯୋଗାଯୋଗଗୁଡ଼ିକ ତିଆରି ହୋଇଛି, ସେଗୁଡ଼ିକୁ ଆପ୍ସର ଆକ୍ସେସ୍ ରହିବ। ଆପଣ ଇନ୍ଷ୍ଟଲ୍ କରିଥିବା ଆପ୍ସ ମାଧ୍ୟମରେ ତିଆରି କରାଯାଇଥିବା ଆକାଉଣ୍ଟଗୁଡ଼ିକୁ ଏହା ସାମିଲ କରିପାରେ। ଏହି ଅନୁମତି ଆପ୍ସକୁ ଆପଣଙ୍କର ଯୋଗାଯୋଗ ଡାଟା ସେଭ୍ କରିବାକୁ ଦିଏ ଏବଂ ହାନୀକାରକ ଆପ୍ ଆପଣଙ୍କ ଅଜାଣତରେ ଯୋଗାଯୋଗ ଡାଟା ସେୟାର୍ କରିପାରେ।"
@@ -409,17 +407,17 @@
"ଏହି ଆପ୍ ଆପଣଙ୍କ କଲ୍ ହିଷ୍ଟୋରୀ ପଢ଼ିପାରେ।""କଲ୍ ଲଗ୍ ଲେଖନ୍ତୁ""ଇନ୍କମିଙ୍ଗ ତଥା ଆଉଟ୍ଗୋଇଙ୍ଗ କଲ୍ ଡାଟା ସହ ଆପଣଙ୍କ ଟାବ୍ଲେଟ୍ର କଲ୍ ଲଗ୍ ବଦଳାଇବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଏ। ହାନୀକାରକ ଆପ୍ ଆପଣଙ୍କ କଲ୍ ଲଗ୍ ଲିଭାଇବାକୁ କିମ୍ବା ବଦଳାଇବାକୁ ଏହା ବ୍ୟବହାର କରିପାରନ୍ତି।"
- "ଇନ୍କମିଂ ତଥା ଆଉଟ୍ଗୋଇଂ କଲ୍ ଡାଟା ସହ ଆପଣଙ୍କ Android ଟିଭି ଡିଭାଇସ୍ର କଲ୍ ଲଗ୍ ସଂଶୋଧନ କରିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। କ୍ଷତିକାରକ ଆପ୍ଗୁଡ଼ିକ ଆପଣଙ୍କ କଲ୍ ଲଗ୍ ଲିଭାଇବାକୁ କିମ୍ବା ସଂଶୋଧନ କରିବା ପାଇଁ ଏହାକୁ ବ୍ୟବହାର କରିପାରନ୍ତି।"
+ "ଇନ୍କମିଂ ତଥା ଆଉଟ୍ଗୋଇଂ କଲ୍ ଡାଟା ସହ ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍ର କଲ୍ ଲଗ୍ ସଂଶୋଧନ କରିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। କ୍ଷତିକାରକ ଆପ୍ଗୁଡ଼ିକ ଆପଣଙ୍କ କଲ୍ ଲଗ୍ ଲିଭାଇବାକୁ କିମ୍ବା ସଂଶୋଧନ କରିବା ପାଇଁ ଏହାକୁ ବ୍ୟବହାର କରିପାରନ୍ତି।""ଇନ୍କମିଙ୍ଗ ତଥା ଆଉଟ୍ଗୋଇଙ୍ଗ କଲ୍ ଡାଟା ସହ ଆପଣଙ୍କ ଫୋନ୍ର କଲ୍ ଲଗ୍ ବଦଳାଇବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଏ। ହାନୀକାରକ ଆପ୍ ଆପଣଙ୍କ କଲ୍ ଲଗ୍ ଲିଭାଇବାକୁ କିମ୍ବା ବଦଳାଇବାକୁ ଏହା ବ୍ୟବହାର କରିପାରନ୍ତି।""ବଡୀ ସେନ୍ସର୍ ଆକ୍ସେସ୍ କରେ (ଯେପରିକି ହୃଦ୍ ହାର ମନିଟର୍)""ଆପ୍କୁ ସେନ୍ସର୍ ଡେଟା ପର୍ଯ୍ୟନ୍ତ ପହଞ୍ଚିବାକୁ ଦେଇଥାଏ, ଯାହା ଆପଣଙ୍କ ଶାରୀରିକ ସ୍ଥିତିର ନିରୀକ୍ଷଣ କରିଥାଏ, ଯେପରିକି ଆପଣଙ୍କ ହୃଦୟ ସ୍ତର।""କ୍ୟାଲେଣ୍ଡର୍ ଇଭେଣ୍ଟ ଏବଂ ବିବରଣୀ ପଢ଼େ""ଆପଣଙ୍କ ଟାବଲେଟ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ସମସ୍ତ କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟ ଏହି ଆପ୍ ପଢ଼ିପାରେ ଏବଂ ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର ଡାଟା ସେୟାର୍ କରିପାରେ କିମ୍ବା ସେଭ୍ କରିପାରେ।"
- "ଏହି ଆପ୍ ଆପଣଙ୍କ Android ଟିଭି ଡିଭାଇସ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ସମସ୍ତ କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟ ପଢ଼ିପାରେ ଏବଂ ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର ଡାଟା ସେୟାର୍ କରିପାରେ କିମ୍ବା ସେଭ୍ କରିପାରେ।"
+ "ଏହି ଆପ୍ ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ସମସ୍ତ କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟ ପଢ଼ିପାରେ ଏବଂ ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର ଡାଟା ସେୟାର୍ କରିପାରେ କିମ୍ବା ସେଭ୍ କରିପାରେ।""ଆପଣଙ୍କ ଫୋନ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ସମସ୍ତ କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟ ଏହି ଆପ୍ ପଢ଼ିପାରେ ଏବଂ ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର ଡାଟା ସେୟାର୍ କରିପାରେ କିମ୍ବା ସେଭ୍ କରିପାରେ।""କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟରେ ଯୋଡ଼ନ୍ତୁ କିମ୍ବା ସଂଶୋଧନ କରନ୍ତୁ ଏବଂ ମାଲିକଙ୍କ ଅଜାଣତରେ ଅତିଥିମାନଙ୍କୁ ଇମେଲ୍ ପଠାନ୍ତୁ।""ଏହି ଆପ୍ ଆପଣଙ୍କ ଟାବଲେଟ୍ରେ କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟ ଯୋଡ଼ିପାରେ, ବାହାର କରିପାରେ କିମ୍ବା ବଦଳାଇପାରେ। ଏହି ଆପ୍ ଏପରି ମେସେଜ୍ ପଠାଇପାରେ, ଯାହା କ୍ୟାଲେଣ୍ଡର ମାଲିକଙ୍କଠାରୁ ଆସିଥିବା ପରି ଜଣାପଡ଼େ କିମ୍ବା ମାଲିକଙ୍କୁ ନଜଣାଇ ଇଭେଣ୍ଟରେ ପରିବର୍ତ୍ତନ କରିପାରେ।"
- "ଏହି ଆପ୍, ଆପଣଙ୍କ Android ଟିଭି ଡିଭାଇସ୍ରେ କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟଗୁଡ଼ିକୁ ଯୋଗ କରିପାରେ, କାଢ଼ି ପାରେ କିମ୍ବା ପରିବର୍ତ୍ତନ କରିପାରେ। ଏହି ଆପ୍ ଏପରି ମେସେଜ୍ ପଠାଇ ପାରେ, ଯାହା କ୍ୟାଲେଣ୍ଡର ମାଲିକଙ୍କଠାରୁ ଆସିଥିବା ପରି ଜଣାପଡ଼େ କିମ୍ବା ମାଲିକଙ୍କୁ ନଜଣାଇ ଇଭେଣ୍ଟରେ ପରିବର୍ତ୍ତନ କରିପାରେ।"
+ "ଏହି ଆପ୍, ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍ରେ କ୍ୟାଲେଣ୍ଡର ଇଭେଣ୍ଟଗୁଡ଼ିକୁ ଯୋଗ କରିପାରେ, କାଢ଼ି ପାରେ କିମ୍ବା ପରିବର୍ତ୍ତନ କରିପାରେ। ଏହି ଆପ୍ ଏପରି ମେସେଜ୍ ପଠାଇ ପାରେ, ଯାହା କ୍ୟାଲେଣ୍ଡର ମାଲିକଙ୍କଠାରୁ ଆସିଥିବା ପରି ଜଣାପଡ଼େ କିମ୍ବା ମାଲିକଙ୍କୁ ନଜଣାଇ ଇଭେଣ୍ଟରେ ପରିବର୍ତ୍ତନ କରିପାରେ।""ଏହି ଆପ୍, ଆପଣଙ୍କ ଫୋନ୍ରେ କ୍ୟାଲେଣ୍ଡର୍ ଇଭେଣ୍ଟଗୁଡ଼ିକୁ ଯୋଡ଼ିପାରେ, ବାହାର କରିପାରେ କିମ୍ବା ବଦଳାଇପାରେ। କ୍ୟାଲେଣ୍ଡର୍ ମାଲିକଙ୍କ ପାଖରୁ ଆସିଥିବା ପରି ଜଣାପଡ଼ିବା ମେସେଜ୍କୁ ଏହି ଆପ୍ ପଠାଇପାରେ କିମ୍ବା ମାଲିକଙ୍କୁ ନଜଣାଇ ଇଭେଣ୍ଟ ବଦଳାଇପାରେ।""ଅତିରିକ୍ତ ଲୋକେସନ୍ ପ୍ରଦାନକାରୀ କମାଣ୍ଡକୁ ଆକ୍ସେସ୍ କରନ୍ତୁ""ଅତିରିକ୍ତ ଲୋକେସନ୍ ପ୍ରଦାନକାରୀ କମାଣ୍ଡ ଆକ୍ସେସ୍ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଏ। GPS କିମ୍ବା ଅନ୍ୟ ଲୋକେସନ୍ ସୋର୍ସଗୁଡିକରେ ଆପ୍ଟି ପ୍ରଭାବ ପକାଇପାରେ।"
@@ -464,7 +462,7 @@
"ଏହି ଡିଭାଇସର ଫୋନ୍ ନମ୍ବର ଆକ୍ସେସ୍ କରିବାକୁ ଆପକୁ ଅନୁମତି ଦିଏ।""କାର ସ୍କ୍ରିନକୁ ଚାଲୁ ରଖନ୍ତୁ""ଟାବଲେଟ୍କୁ ସ୍ଲୀପିଙ୍ଗ ମୋଡ୍କୁ ଯିବାକୁ ରୋକନ୍ତୁ"
- "ସ୍ଲିପିଂରୁ ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ପ୍ରତିରୋଧ କରନ୍ତୁ"
+ "ସ୍ଲିପିଂରୁ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ପ୍ରତିରୋଧ କରନ୍ତୁ""ଫୋନକୁ ସ୍ଲୀପିଙ୍ଗ ମୋଡ୍କୁ ଯିବାକୁ ରୋକନ୍ତୁ""କାର ସ୍କ୍ରିନକୁ ଚାଲୁ ରଖିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦେଇଥାଏ।""ଆପ୍କୁ, ଟାବଲେଟ୍ଟିକୁ ସ୍ଲୀପ୍ ମୋଡ୍କୁ ଯିବାରେ ପ୍ରତିରୋଧ କରିବାକୁ ଦେଇଥାଏ।"
@@ -472,7 +470,7 @@
"ଆପ୍କୁ, ଫୋନ୍ଟିକୁ ସ୍ଲୀପ୍ ମୋଡ୍କୁ ଯିବାରେ ପ୍ରତିରୋଧ କରିବାକୁ ଦେଇଥାଏ।""ଇନଫ୍ରାରେଡ୍ ସଂଚାରିତ କରନ୍ତୁ""ଟାବଲେଟ୍ର ଇନଫ୍ରାରେଡ୍ ଟ୍ରାନ୍ସମିଟର୍ ବ୍ୟବହାର କରିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
- "ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ର ଇନ୍ଫ୍ରାରେଡ୍ ଟ୍ରାନ୍ସମିଟର୍ ବ୍ୟବହାର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
+ "ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ର ଇନ୍ଫ୍ରାରେଡ୍ ଟ୍ରାନ୍ସମିଟର୍ ବ୍ୟବହାର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।""ଫୋନ୍ର ଇନଫ୍ରାରେଡ୍ ଟ୍ରାନ୍ସମିଟର୍ ବ୍ୟବହାର କରିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।""ୱାଲପେପର୍ ସେଟ୍ କରନ୍ତୁ""ଆପ୍କୁ, ସିଷ୍ଟମ୍ ୱାଲପେପର୍ ସେଟ୍ କରିବାକୁ ଦେଇଥାଏ।"
@@ -480,11 +478,11 @@
"ଆପ୍କୁ, ସିଷ୍ଟମ୍ ୱାଲପେପର୍ ଆକାରର ସୂଚନା ସେଟ୍ କରିବାକୁ ଦେଇଥାଏ।""ଟାଇମ୍ ଜୋନ୍ ସେଟ୍ କରନ୍ତୁ""ଆପ୍କୁ, ଟାବଲେଟ୍ର ଟାଇମ୍ ଜୋନ୍ ବଦଳାଇବାକୁ ଦେଇଥାଏ।"
- "ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ର ଟାଇମ୍ ଜୋନ୍ ପରିବର୍ତ୍ତନ କରିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
+ "ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ର ଟାଇମ୍ ଜୋନ୍ ପରିବର୍ତ୍ତନ କରିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।""ଆପ୍କୁ, ଫୋନ୍ର ଟାଇମ୍ ଜୋନ୍ ବଦଳାଇବାକୁ ଦେଇଥାଏ।""ଡିଭାଇସ୍ରେ ଆକାଉଣ୍ଟ ଖୋଜନ୍ତୁ""ଟାବଲେଟ୍ ଦ୍ୱାରା ପରିଚିତ ଆକାଉଣ୍ଟର ତାଲିକା ପ୍ରାପ୍ତ କରିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। ଆପଣ ଇନଷ୍ଟଲ୍ କରିଥିବା ଆପ୍ଲିକେଶନ୍ ଦ୍ୱାରା ତିଆରି କରାଯାଇଥିବା କୌଣସି ଆକାଉଣ୍ଟ ବି ଏଥିରେ ସାମିଲ୍ ହୋଇପାରେ।"
- "ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଜଣାଥିବା ଆକାଉଣ୍ଟଗୁଡ଼ିକର ତାଲିକା ପାଇବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। ଆପଣ ଇନ୍ଷ୍ଟଲ୍ କରିଥିବା ଆପ୍ଲିକେସନ୍ ମାଧ୍ୟମରେ ଏହା ଯେ କୌଣସି ଆକାଉଣ୍ଟକୁ ହୁଏତ ଅନ୍ତର୍ଭୁକ୍ତ କରିପାରେ।"
+ "ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଜଣାଥିବା ଆକାଉଣ୍ଟଗୁଡ଼ିକର ତାଲିକା ପାଇବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। ଆପଣ ଇନ୍ଷ୍ଟଲ୍ କରିଥିବା ଆପ୍ଲିକେସନ୍ ମାଧ୍ୟମରେ ଏହା ଯେ କୌଣସି ଆକାଉଣ୍ଟକୁ ଅନ୍ତର୍ଭୁକ୍ତ କରିପାରେ।""ଫୋନ୍ ଦ୍ୱାରା ପରିଚିତ ଆକାଉଣ୍ଟର ତାଲିକା ପ୍ରାପ୍ତ କରିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। ଆପଣ ଇନଷ୍ଟଲ୍ କରିଥିବା ଆପ୍ଲିକେଶନ୍ ଦ୍ୱାରା ତିଆରି କରାଯାଇଥିବା କୌଣସି ଆକାଉଣ୍ଟ ବି ଏଥିରେ ସାମିଲ୍ ହୋଇପାରେ।""ନେଟ୍ୱର୍କ ସଂଯୋଗ ଦେଖନ୍ତୁ""କେଉଁ ନେଟ୍ୱର୍କ ଉପସ୍ଥିତ ତଥା ସଂଯୁକ୍ତ ଅଛି, ତାହା ବିଷୟରେ ସୂଚନା ଦେଖିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
@@ -500,21 +498,21 @@
"ଆପ୍କୁ ୱାଇ-ଫାଇ ଆକ୍ସେସ୍ ପଏଣ୍ଟ ସହିତ ସଂଯୋଗ ଓ ବିଚ୍ଛିନ୍ନ କରିବାକୁ ତଥା ୱାଇ-ଫାଇ ନେଟ୍ୱର୍କ ପାଇଁ ଡିଭାଇସ୍ କନଫିଗରେଶନ୍ରେ ପରିବର୍ତ୍ତନ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ।""ୱାଇ-ଫାଇ ମଲ୍ଟିକାଷ୍ଟ ରିସେପଶନ ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ""କେବଳ ଆପଣଙ୍କ ଟାବ୍ଲେଟ୍ ନୁହେଁ, ବରଂ ମଲ୍ଟିକାଷ୍ଟ ଠିକଣାଗୁଡ଼ିକ ବ୍ୟବହାର କରି ଏକ ୱାଇ-ଫାଇ ନେଟ୍ୱର୍କରେ ଥିବା ସମସ୍ତ ଡିଭାଇସ୍କୁ ପଠାଯିବା ପ୍ୟାକେଟ୍ଗୁଡ଼ିକ ପ୍ରାପ୍ତ କରିବାକୁ ଆପ୍ଟି ଅନୁମତି ଦେଇଥାଏ। ଅଣ-ମଲ୍ଟିକାଷ୍ଟ ମୋଡ୍ ତୁଳନାରେ ଏହା ଅଧିକ ପାୱାର୍ ବ୍ୟବହାର କରେ।"
- "କେବଳ ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ ନୁହେଁ, ମଲ୍ଟିକାଷ୍ଟ ଠିକଣାଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି ଏକ ୱାଇ-ଫାଇ ନେଟ୍ୱାର୍କରେ ସମସ୍ତ ଡିଭାଇସ୍କୁ ପଠାଯାଇଥିବା ପ୍ୟାକେଟ୍ ପ୍ରାପ୍ତ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। ଏହା ମଲ୍ଟିକାଷ୍ଟ ମୋଡ୍ ନଥିବା ତୁଳନାରେ ଅଧିକ ପାୱାର୍ ବ୍ୟବହାର କରିଥାଏ।"
+ "କେବଳ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ ନୁହେଁ, ମଲ୍ଟିକାଷ୍ଟ ଠିକଣାଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି ଏକ ୱାଇ-ଫାଇ ନେଟ୍ୱାର୍କରେ ସମସ୍ତ ଡିଭାଇସ୍କୁ ପଠାଯାଇଥିବା ପ୍ୟାକେଟ୍ ପ୍ରାପ୍ତ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। ଏହା ମଲ୍ଟିକାଷ୍ଟ ମୋଡ୍ ନଥିବା ତୁଳନାରେ ଅଧିକ ପାୱାର୍ ବ୍ୟବହାର କରିଥାଏ।""କେବଳ ଆପଣଙ୍କ ଫୋନ୍ ନୁହେଁ, ବରଂ ମଲ୍ଟିକାଷ୍ଟ ଠିକଣାଗୁଡ଼ିକ ବ୍ୟବହାର କରି ଏକ ୱାଇ-ଫାଇ ନେଟ୍ୱର୍କରେ ଥିବା ସମସ୍ତ ଡିଭାଇସ୍କୁ ପଠାଯିବା ପ୍ୟାକେଟ୍ଗୁଡ଼ିକ ପ୍ରାପ୍ତ କରିବାକୁ ଆପ୍ଟି ଅନୁମତି ଦେଇଥାଏ। ଅଣ-ମଲ୍ଟିକାଷ୍ଟ ମୋଡ୍ ତୁଳନାରେ ଏହା ଅଧିକ ପାୱାର୍ ବ୍ୟବହାର କରେ।""ବ୍ଲୁଟୂଥ୍ ସେଟିଙ୍ଗ ଆକ୍ସେସ୍ କରନ୍ତୁ""ସ୍ଥାନୀୟ ବ୍ଲୁ-ଟୁଥ, ଟାବଲେଟ୍କୁ କନଫିଗର୍ କରିବାକୁ ଏବଂ ରିମୋର୍ଟ ଡିଭାଇସ୍କୁ ଚିହ୍ନାଇବା ତଥା ସେଗୁଡ଼ିକୁ ପେୟାର୍ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
- "ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ରେ ବ୍ଲୁଟୁଥ୍ର କନଫିଗର୍ କରିବା ପାଇଁ ଏବଂ ରିମୋଟ୍ ଡିଭାଇସ୍ଗୁଡ଼ିକୁ ଖୋଜିବା ସହ ପେୟାର୍ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
+ "ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ରେ ବ୍ଲୁଟୁଥ୍ର କନଫିଗର୍ କରିବା ପାଇଁ ଏବଂ ରିମୋଟ୍ ଡିଭାଇସ୍ଗୁଡ଼ିକୁ ଖୋଜିବା ସହ ପେୟାର୍ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।""ସ୍ଥାନୀୟ ବ୍ଲୁ-ଟୁଥ, ଫୋନ୍କୁ କନଫିଗର୍ କରିବାକୁ ଏବଂ ରିମୋର୍ଟ ଡିଭାଇସ୍କୁ ଚିହ୍ନାଇବା ତଥା ସେଗୁଡ଼ିକୁ ପେୟାର୍ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।""WiMAX ସହିତ ସଂଯୋଗ ଏବଂ ଏଥିରୁ ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ""WiMAX ସକ୍ଷମ କି ନାହିଁ ସ୍ଥିର କରିବାକୁ ଏବଂ ସଂଯୁକ୍ତ ଥିବା କୌଣସି WiMAX ନେଟ୍ୱର୍କ ବିଷୟରେ ସୂଚନା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ ।""WiMAX ସ୍ଥିତିକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ""WiMAX ନେଟ୍ୱର୍କରୁ ଟାବଲେଟ୍ ସଂଯୋଗ କରିବାକୁ ଏବଂ ବିଚ୍ଛିନ୍ନ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
- "WiMAX ନେଟ୍ୱାର୍କରୁ ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ ସଂଯୋଗ କରିବାକୁ ଏବଂ ବିଚ୍ଛିନ୍ନ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
+ "WiMAX ନେଟ୍ୱାର୍କରୁ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ ସଂଯୋଗ କରିବାକୁ ଏବଂ ବିଚ୍ଛିନ୍ନ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।""WiMAX ନେଟ୍ୱର୍କରୁ ଫୋନ୍ ସଂଯୋଗ କରିବାକୁ ଏବଂ ବିଚ୍ଛିନ୍ନ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।""ବ୍ଲୁଟୂଥ୍ ଡିଭାଇସ୍ ଦେଖନ୍ତୁ""ଟାବଲେଟ୍ରେ ଥିବା ବ୍ଲୁ-ଟୁଥ୍ର କନଫିଗରେଶନ୍ ଦେଖିବାକୁ ଏବଂ ପେୟାର୍ କରାଯାଇଥିବା ଡିଭାଇସ୍ ସହିତ ସଂଯୋଗ ସ୍ୱୀକାର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
- "ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ରେ ବ୍ଲୁଟୁଥ୍ର କନଫିଗ୍ରେସନ୍ ଦେଖିବା ପାଇଁ ଏବଂ ପେୟାର୍ କରାଯାଇଥିବା ଡିଭାଇସ୍ଗୁଡ଼ିକ ସହ ସଂଯୋଗଗୁଡ଼ିକୁ ତିଆରି ଏବଂ ସ୍ୱୀକାର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"
+ "ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ରେ ବ୍ଲୁଟୁଥ୍ର କନଫିଗ୍ରେସନ୍ ଦେଖିବା ପାଇଁ ଏବଂ ପେୟାର୍ କରାଯାଇଥିବା ଡିଭାଇସ୍ଗୁଡ଼ିକ ସହ ସଂଯୋଗଗୁଡ଼ିକୁ ତିଆରି ଏବଂ ସ୍ୱୀକାର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।""ଫୋନ୍ରେ ଥିବା ବ୍ଲୁ-ଟୁଥ୍ର କନଫିଗରେଶନ୍ ଦେଖିବାକୁ ଏବଂ ପେୟାର୍ କରାଯାଇଥିବା ଡିଭାଇସ୍ ସହିତ ସଂଯୋଗ ସ୍ୱୀକାର କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।""ପସନ୍ଦର NFC ପେମେଣ୍ଟ ସେବା ସୂଚନା""ପଞ୍ଜିକୃତ ଯନ୍ତ୍ର ଏବଂ ମାର୍ଗ ଲକ୍ଷସ୍ଥଳ ପରି ପସନ୍ଦର nfc ପେମେଣ୍ଟ ସେବା ସୂଚନା ପାଇବାକୁ ଆପ୍ ଅନୁମତି କରିଥାଏ।"
@@ -675,10 +673,10 @@
"ଲକ୍ ସ୍କ୍ରୀନ୍ ପାସ୍ୱର୍ଡ ଓ PINରେ ଅନୁମୋଦିତ ଦୀର୍ଘତା ଓ ବର୍ଣ୍ଣ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ।""ସ୍କ୍ରୀନ୍-ଅନଲକ୍ କରିବା ଉଦ୍ୟମ ନୀରିକ୍ଷଣ କରନ୍ତୁ""ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଟାବଲେଟ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"
- "ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରେ ଏବଂ ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଆପଣଙ୍କ Android ଟିଭି ଡିଭାଇସ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦେଇଥାଏ।"
+ "ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଲକ୍ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।""ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରିଥିବା ଭୁଲ ପାସୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଫୋନ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ଫୋନ୍ର ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।""ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଟାବଲେଟ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।"
- "ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରେ ଏବଂ ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଏ।"
+ "ସ୍କ୍ରିନ୍ ଅନ୍ଲକ୍ କରିବା ସମୟରେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱାର୍ଡଗୁଡ଼ିକର ସଂଖ୍ୟାକୁ ନିରୀକ୍ଷଣ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଲକ୍ କରନ୍ତୁ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱାର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।""ସ୍କ୍ରୀନ୍ ଅନଲକ୍ କରିବାବେଳେ ଟାଇପ୍ କରାଯାଇଥିବା ଭୁଲ ପାସ୍ୱର୍ଡର ସଂଖ୍ୟାକୁ ନୀରିକ୍ଷଣ କରେ ଏବଂ ଫୋନ୍କୁ ଲକ୍ କରିଦିଏ କିମ୍ବା ଯଦି ଅନେକ ଭୁଲ ପାସ୍ୱର୍ଡ ଟାଇପ୍ କରାଯାଇଥାଏ, ତେବେ ସମସ୍ତ ଡାଟା ଲିଭାଇଦିଏ।""ସ୍କ୍ରିନ୍ ଲକ୍ ବଦଳାଏ""ସ୍କ୍ରିନ୍ ଲକ୍ ବଦଳାଏ।"
@@ -686,11 +684,11 @@
"ସ୍କ୍ରିନ୍ କିପରି ଓ କେତେବେଳେ ଲକ୍ କରାଯିବ, ତାହା ନିୟନ୍ତ୍ରଣ କରେ।""ସମସ୍ତ ଡାଟା ଖାଲି କରିବା""ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ସେଟିଙ୍ଗ କରାଇ ଟାବ୍ଲେଟ୍ର ଡାଟା ଲିଭାଇଥାଏ।"
- "ଏକ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ କରି ବିନା ଆଲର୍ଟରେ ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ର ଡାଟା ଲିଭାନ୍ତୁ।"
+ "ଏକ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ କରି ବିନା ଚେତାବନୀରେ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ର ଡାଟା ଲିଭାନ୍ତୁ।""ବିନା ଚେତାବନୀରେ ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍ କରି ଫୋନ୍ର ଡାଟା ଲିଭାଇଥାଏ।""ୟୁଜର୍ ଡାଟା ଲିଭାନ୍ତୁ""ବିନା ଚେତାବନୀରେ ଏହି ଟାବଲେଟରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"
- "ବିନା ଚେତାବନୀରେ ଏହି Android ଟିଭି ଡିଭାଇସ୍ରେ ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।"
+ "ବିନା ଚେତାବନୀରେ ଏହି Android TV ଡିଭାଇସ୍ରେ ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।""ବିନା ଚେତାବନୀରେ ଏହି ଫୋନରେ ଥିବା ଏହି ୟୁଜରଙ୍କ ଡାଟା ଲିଭାଇ ଦିଅନ୍ତୁ।""ଗ୍ଲୋବଲ୍ ପ୍ରକ୍ସୀ ଡିଭାଇସ୍କୁ ସେଟ୍ କରନ୍ତୁ""ପଲିସୀ ସକ୍ଷମ କରାଯାଇଥିବାବେଳେ ବ୍ୟବହାର କରିବା ପାଇଁ ଗ୍ଲୋବାଲ୍ ପ୍ରକ୍ସୀ ସେଟ୍ କରନ୍ତୁ। କେବଳ ଡିଭାଇସ୍ ମାଲିକ ଗ୍ଲୋବାଲ୍ ପ୍ରକ୍ସୀ ସେଟ୍ କରିପାରିବେ।"
@@ -789,7 +787,7 @@
"Yahoo""Skype""QQ"
- "ହ୍ୟାଙ୍ଗଆଉଟ୍ସ"
+ "Hangouts""ICQ""Jabber""NetMeeting"
@@ -840,7 +838,7 @@
"ମାଲିକର ମୁହଁ ଚିହ୍ନି ଅନଲକ୍ କରିବାର ସର୍ବାଧିକ ଧାର୍ଯ୍ୟ ସୀମା ଅତିକ୍ରମ କଲା""କୌଣସି SIM କାର୍ଡ ନାହିଁ""ଟାବଲେଟ୍ରେ କୌଣସି SIM କାର୍ଡ ନାହିଁ।"
- "ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ରେ କୌଣସି SIM କାର୍ଡ ନାହିଁ।"
+ "ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ରେ କୌଣସି SIM କାର୍ଡ ନାହିଁ।""ଫୋନ୍ରେ କୌଣସି SIM କାର୍ଡ ନାହିଁ।""ଏକ SIM କାର୍ଡ ଭର୍ତ୍ତି କରନ୍ତୁ।""SIM କାର୍ଡ ନାହିଁ କିମ୍ବା ଖରାପ ଅଛି। SIM କାର୍ଡ ଭର୍ତ୍ତି କରନ୍ତୁ।"
@@ -863,13 +861,13 @@
"ଆପଣଙ୍କ ପାସୱର୍ଡକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଟାଇପ୍ କରିଛନ୍ତି। \n\n%2$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।""ଆପଣଙ୍କ PINକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଟାଇପ୍ କରିଛନ୍ତି। \n\n%2$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।""ଅନଲକ୍ ପାଟର୍ନକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, Google ସାଇନ୍-ଇନ୍ ବ୍ୟବହାର କରି ଆପଣଙ୍କୁ ନିଜ ଟାବଲେଟ୍କୁ ଅନଲକ୍ କରିବାକୁ କୁହାଯିବ।\n\n %3$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"
- "ଆପଣ ଆପଣଙ୍କର ଲକ୍ ଖୋଲିବା ପାଟର୍ନକୁ %1$d ଥର ଭୁଲ ଆଙ୍କିଛନ୍ତି। %2$d ଥର ଅସଫଳ ଚେଷ୍ଟା ପରେ, ଆପଣଙ୍କୁ Google ସାଇନ୍ଇନ୍ ବ୍ୟବହାର କରି ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଅନ୍ଲକ୍ କରିବା ପାଇଁ କୁହାଯିବ। \n\n%3$d ସେକେଣ୍ଡ ମଧ୍ୟରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"
+ "ଆପଣ ଆପଣଙ୍କର ଅନଲକ୍ ପାଟର୍ନକୁ %1$d ଥର ଭୁଲ ଭାବେ ଆଙ୍କିଛନ୍ତି। %2$d ଥର ଅସଫଳ ଚେଷ୍ଟା ପରେ, ଆପଣଙ୍କୁ Google ସାଇନ୍ଇନ୍ ବ୍ୟବହାର କରି ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଅନ୍ଲକ୍ କରିବା ପାଇଁ କୁହାଯିବ। \n\n%3$d ସେକେଣ୍ଡ ମଧ୍ୟରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।""ଅନଲକ୍ ପାଟର୍ନକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, Google ସାଇନ୍-ଇନ୍ ବ୍ୟବହାର କରି ଆପଣଙ୍କୁ ନିଜ ଫୋନ୍କୁ ଅନଲକ୍ କରିବାକୁ କୁହାଯିବ।\n\n %3$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।""ଟାବଲେଟ୍କୁ ଅନ୍ଲକ୍ କରିବା ପାଇଁ %1$d ଥର ଭୁଲ ପ୍ରୟାସ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, ଟାବଲେଟ୍ଟି ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ ଏବଂ ଆପଣ ସମସ୍ତ ୟୁଜର୍ ଡାଟା ହରାଇବେ।"
- "ଆପଣ %1$d ଥର ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଭୁଲ ଭାବେ ଅନ୍ଲକ୍ କରିବାକୁ ଚେଷ୍ଟା କରିଛନ୍ତି। %2$d ଥର ଅସଫଳ ଚେଷ୍ଟା ପରେ, ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ ଡିଫଲ୍ଟକୁ ଫ୍ୟାକ୍ଟୋରୀ ରିସେଟ୍ କରାଯିବ ଏବଂ ସମସ୍ତ ଉପଯୋଗକର୍ତ୍ତା ଡାଟା ହରାଇବ।"
+ "ଆପଣ %1$d ଥର ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଭୁଲ ଭାବେ ଅନ୍ଲକ୍ କରିବାକୁ ଚେଷ୍ଟା କରିଛନ୍ତି। %2$d ଥର ଅସଫଳ ଚେଷ୍ଟା ପରେ, ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ କରାଯିବ ଏବଂ ସମସ୍ତ ଉପଯୋଗକର୍ତ୍ତା ଡାଟା ହରାଇବ।""ଫୋନ୍ ଅନଲକ୍ କରିବାକୁ ଆପଣ %1$d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, ଫୋନ୍ଟି ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ ଏବଂ ଆପଣ ସମସ୍ତ ୟୁଜର୍ ଡାଟା ହରାଇବେ।""ଟାବଲେଟ୍ ଅନଲକ୍ କରିବାକୁ ଆପଣ %d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଟାବଲେଟ୍ଟି ବର୍ତ୍ତମାନ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ।"
- "ଆପଣ %d ଥର ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଭୁଲ ଭାବେ ଅନ୍ଲକ୍ କରିବାକୁ ଚେଷ୍ଟା କରିଛନ୍ତି। ବର୍ତ୍ତମାନ ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ।"
+ "ଆପଣ %d ଥର ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଭୁଲ ଭାବେ ଅନ୍ଲକ୍ କରିବାକୁ ଚେଷ୍ଟା କରିଛନ୍ତି। ବର୍ତ୍ତମାନ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ।""ଫୋନ୍ ଅନଲକ୍ କରିବାକୁ ଆପଣ %d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଫୋନ୍ଟି ବର୍ତ୍ତମାନ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ।""%d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।""ପାଟର୍ନ ଭୁଲି ଯାଇଛନ୍ତି କି?"
@@ -956,7 +954,7 @@
"ବ୍ରାଉଜର୍ରେ ଭିଜିଟ୍ କରାଯାଇଥିବା ସମସ୍ତ URL ପଢ଼ିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଏ। ଧ୍ୟାନଦିଅନ୍ତୁ: ଏହି ଅନୁମତି ୱେବ୍ ବ୍ରାଉଜ୍ କରିବା ଦକ୍ଷତା ତୃତୀୟ-ପକ୍ଷ ବ୍ରାଉଜର୍ କିମ୍ବା ଅନ୍ୟାନ୍ୟ ଆପ୍ଲିକେଶନ୍ରେ ଲାଗୁ କରାଯାଇନପାରେ।""ୱେବ୍ ବୁକ୍ମାର୍କ ଓ ହିଷ୍ଟୋରୀ ଲେଖନ୍ତୁ""ଆପଣଙ୍କ ଟାବ୍ଲେଟ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ବ୍ରାଉଜର୍ ହିଷ୍ଟୋରୀ କିମ୍ବା ବୁକ୍ମାର୍କଗୁଡ଼ିକ ବଦଳାଇବାକୁ ଆପ୍କୁ ଅନୁମତି ଦିଏ। ଏହାଦ୍ୱାରା ଆପ୍ଟି ବ୍ରାଉଜର୍ ଡାଟା ଲିଭାଇପାରେ କିମ୍ବା ବଦଳାଇପାରେ। ଧ୍ୟାନଦିଅନ୍ତୁ: ଏହି ଅନୁମତି ୱେବ୍ ବ୍ରାଉଜ୍ କରିବାର ଦକ୍ଷତା ତୃତୀୟ-ପକ୍ଷ ବ୍ରାଉଜର୍ କିମ୍ବା ଅନ୍ୟାନ୍ୟ ଆପ୍ଲିକେଶନ୍ରେ ଲାଗୁ କରାଯାଇନପାରେ।"
- "ଆପଣଙ୍କ Android ଟିଭି ଡିଭାଇସ୍ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ବ୍ରାଉଜର୍ ଇତିହାସ କିମ୍ବା ବୁକମାର୍କଗୁଡ଼ିକ ସଂଶୋଧନ ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। ଏହା ଦ୍ୱାରା ଆପ୍ ବ୍ରାଉଜର୍ ଡାଟା ଲିଭାଇ ପାରେ କିମ୍ବା ସଂଶୋଧନ କରିପାରେ। ଧ୍ୟାନ ଦିଅନ୍ତୁ: ଏହି ଅନୁମତି ହୁଏତ ୱେବ୍ ବ୍ରାଉଜିଂ ଦକ୍ଷତା ସହ ତୃତୀୟ-ପକ୍ଷ ବ୍ରାଉଜର୍ କିମ୍ବା ଅନ୍ୟ ଆପ୍ଲିକେସନ୍ରେ ଲାଗୁ କରାଯାଇ ନପାରେ।"
+ "ଆପଣଙ୍କ Android TV ଡିଭାଇସ୍ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ବ୍ରାଉଜର୍ ଇତିହାସ କିମ୍ବା ବୁକମାର୍କଗୁଡ଼ିକୁ ସଂଶୋଧନ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ। ଏହା ଦ୍ୱାରା ଆପ୍ ବ୍ରାଉଜର୍ ଡାଟା ଲିଭାଇ ପାରେ କିମ୍ବା ସଂଶୋଧନ କରିପାରେ। ଧ୍ୟାନ ଦିଅନ୍ତୁ: ଏହି ଅନୁମତି ହୁଏତ ୱେବ୍ ବ୍ରାଉଜିଂ ଦକ୍ଷତା ସହ ତୃତୀୟ-ପକ୍ଷ ବ୍ରାଉଜର୍ କିମ୍ବା ଅନ୍ୟ ଆପ୍ଲିକେସନ୍ରେ ଲାଗୁ କରାଯାଇ ନପାରେ।""ଆପଣଙ୍କ ଫୋନ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇଥିବା ବ୍ରାଉଜର୍ ହିଷ୍ଟୋରୀ କିମ୍ବା ବୁକ୍ମାର୍କଗୁଡ଼ିକ ବଦଳାଇବାକୁ ଆପ୍କୁ ଅନୁମତି ଦିଏ। ଏହାଦ୍ୱାରା ଆପ୍ଟି ବ୍ରାଉଜର୍ ଡାଟା ଲିଭାଇପାରେ କିମ୍ବା ବଦଳାଇପାରେ। ଧ୍ୟାନଦିଅନ୍ତୁ: ଏହି ଅନୁମତି ୱେବ୍ ବ୍ରାଉଜ୍ କରିବାର ଦକ୍ଷତା ତୃତୀୟ-ପକ୍ଷ ବ୍ରାଉଜର୍ କିମ୍ବା ଅନ୍ୟାନ୍ୟ ଆପ୍ଲିକେଶନ୍ରେ ଲାଗୁ କରାଯାଇନପାରେ।""ଏକ ଆଲର୍ମ ସେଟ୍ କରନ୍ତୁ""ଆପ୍କୁ, ଇନଷ୍ଟଲ୍ ହୋଇଥିବା ଆଲାର୍ମ କ୍ଲକ୍ ଆପ୍ରେ ଏକ ଆଲାର୍ମ ସେଟ୍ କରିବାକୁ ଦେଇଥାଏ। କିଛି ଆଲର୍ମ କ୍ଲକ୍ ଆପ୍ ଏହି ବୈଶିଷ୍ଟ୍ୟ ଲାଗୁ କରିନପାରନ୍ତି।"
@@ -1309,7 +1307,7 @@
"ଆନାଲଗ୍ ଅଡିଓ ଆକ୍ସେସରୀ ଚିହ୍ନଟ ହେଲା""ଏହି ଫୋନ୍ରେ କନେକ୍ଟ ଥିବା ଡିଭାଇସ୍ କମ୍ପାଟିବଲ୍ ନୁହେଁ। ଅଧିକ ଜାଣିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।""USB ଡିବଗିଂ ସଂଯୁକ୍ତ ହୋଇଛି"
- "USB ଡିବଗିଂ ସୁବିଧାକୁ ବନ୍ଦ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"
+ "USB ଡିବଗିଂକୁ ବନ୍ଦ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ""USB ଡିବଗିଙ୍ଗକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଚୟନ କରନ୍ତୁ।""ୱାୟାରଲେସ୍ ଡିବଗିଂ ସଂଯୋଗ କରାଯାଇଛି""ୱାୟାରଲେସ୍ ଡିବଗିଂକୁ ବନ୍ଦ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"
@@ -1611,17 +1609,16 @@
"ଆପଣଙ୍କ ପାସ୍ୱର୍ଡକୁ ଆପଣ %1$dଥର ଭୁଲ ଭାବେ ଟାଇପ୍ କରିଛନ୍ତି। %2$d ସେକେଣ୍ଡ ପରେ \n\nପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।""ଆପଣଙ୍କ ଲକ୍ ଖୋଲିବା ପାଟର୍ନକୁ ଆପଣ %1$dଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। %2$d ସେକେଣ୍ଡ ପରେ \n\nପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।""ଟାବଲେଟ୍ ଅନଲକ୍ କରିବାକୁ ଆପଣ %1$d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, ଟାବଲେଟ୍ଟି ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ ଏବଂ ସମସ୍ତ ୟୁଜର୍ ଡାଟା ବାହାରିଯିବ।"
- "ଆପଣ %1$d ଥର ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଭୁଲ ଭାବେ ଅନ୍ଲକ୍ କରିବାକୁ ଚେଷ୍ଟା କରିଛନ୍ତି। %2$d ଥର ଅସଫଳ ଚେଷ୍ଟା ପରେ, ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ ଡିଫଲ୍ଟକୁ ଫ୍ୟାକ୍ଟୋରୀ ରିସେଟ୍ କରାଯିବ ଏବଂ ସମସ୍ତ ଉପଯୋଗକର୍ତ୍ତା ଡାଟା ହରାଇବ।"
+ "ଆପଣ %1$d ଥର ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଭୁଲ ଭାବେ ଅନ୍ଲକ୍ କରିବାକୁ ଚେଷ୍ଟା କରିଛନ୍ତି। %2$d ଥର ଅସଫଳ ଚେଷ୍ଟା ପରେ, ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ କରାଯିବ ଏବଂ ସମସ୍ତ ଉପଯୋଗକର୍ତ୍ତା ଡାଟା ହରାଇବ।""ଫୋନ୍ ଅନଲକ୍ କରିବାକୁ ଆପଣ %1$d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ, ଫୋନ୍ଟି ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ ଏବଂ ସମସ୍ତ ୟୁଜର୍ ଡାଟା ବାହାରିଯିବ।""ଟାବଲେଟ୍ ଅନଲକ୍ କରିବାକୁ ଆପଣ %d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଟାବଲେଟ୍ଟି ବର୍ତ୍ତମାନ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ।"
- "ଆପଣ %d ଥର ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଭୁଲ ଭାବେ ଅନ୍ଲକ୍ କରିବାକୁ ଚେଷ୍ଟା କରିଛନ୍ତି। ବର୍ତ୍ତମାନ ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ।"
+ "ଆପଣ %d ଥର ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଭୁଲ ଭାବେ ଅନ୍ଲକ୍ କରିବାକୁ ଚେଷ୍ଟା କରିଛନ୍ତି। ବର୍ତ୍ତମାନ ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ।""ଫୋନ୍ ଅନଲକ୍ କରିବାକୁ ଆପଣ %d ଥର ଭୁଲ ପ୍ରୟାସ କଲେ। ଫୋନ୍ଟି ବର୍ତ୍ତମାନ ଫ୍ୟାକ୍ଟୋରୀ ଡିଫଲ୍ଟକୁ ରିସେଟ୍ ହୋଇଯିବ।""ଆପଣଙ୍କ ଅନଲକ୍ ପାଟର୍ନକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ ଏକ ଇମେଲ୍ ଆକାଉଣ୍ଟ ବ୍ୟବହାର କରି ନିଜ ଟାବଲେଟ୍କୁ ଅନଲକ୍ କରିବା ପାଇଁ କୁହାଯିବ।\n\n%3$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"
- "ଆପଣ ଆପଣଙ୍କର ଲକ୍ ଖୋଲିବା ପାଟର୍ନକୁ %1$d ଥର ଭୁଲ ଆଙ୍କିଛନ୍ତି। %2$d ଥର ଅସଫଳ ଚେଷ୍ଟା ପରେ, ଏକ ଇମେଲ୍ ଆକାଉଣ୍ଟ ବ୍ୟବହାର କରି ଆପଣଙ୍କର Android ଟିଭି ଡିଭାଇସ୍କୁ ଅନ୍ଲକ୍ କରିବା ପାଇଁ କୁହାଯିବ। \n\n%3$d ସେକେଣ୍ଡ ମଧ୍ୟରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"
+ "ଆପଣ ଆପଣଙ୍କର ଅନଲକ୍ ପାଟର୍ନକୁ %1$d ଥର ଭୁଲ ଭାବେ ଆଙ୍କିଛନ୍ତି। %2$d ଥର ଅସଫଳ ଚେଷ୍ଟା ପରେ, ଏକ ଇମେଲ୍ ଆକାଉଣ୍ଟ ବ୍ୟବହାର କରି ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଅନ୍ଲକ୍ କରିବା ପାଇଁ କୁହାଯିବ। \n\n%3$d ସେକେଣ୍ଡ ମଧ୍ୟରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।""ଆପଣଙ୍କ ଅନଲକ୍ ପାଟର୍ନକୁ ଆପଣ %1$d ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ %2$dଟି ଭୁଲ ପ୍ରୟାସ ପରେ ଏକ ଇମେଲ୍ ଆକାଉଣ୍ଟ ବ୍ୟବହାର କରି ନିଜ ଫୋନ୍କୁ ଅନଲକ୍ କରିବା ପାଇଁ କୁହାଯିବ।\n\n%3$d ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"" — ""ବାହାର କରନ୍ତୁ"
- "ପୃଷ୍ଠଭୂମିରେ %1$sରୁ ଆରମ୍ଭ ହୋଇଥିବା ସମ୍ମୁଖଭାଗ ସେବା ପାଇଁ ଭବିଷ୍ୟତର R ବିଲ୍ଡଗୁଡ଼ିକରେ ବ୍ୟବହାର କରାଯିବା ସମୟରେ ଅନୁମତି ସୁବିଧା ରହିବ ନାହିଁ। ଦୟାକରି go/r-bg-fgs-restriction ଦେଖନ୍ତୁ ଏବଂ ଏକ ବଗରିପୋର୍ଟ ଫାଇଲ୍ କରନ୍ତୁ।""ମାତ୍ରା ବଢ଼ାଇ ସୁପାରିଶ ସ୍ତର ବଢ଼ାଉଛନ୍ତି? \n\n ଲମ୍ବା ସମୟ ପର୍ଯ୍ୟନ୍ତ ଉଚ୍ଚ ଶବ୍ଦରେ ଶୁଣିଲେ ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତି ଖରାପ ହୋଇପାରେ।""ଆକ୍ସେସବିଲିଟି ଶର୍ଟକଟ୍ ବ୍ୟବହାର କରିବେ?""ସର୍ଟକଟ୍ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ଭଲ୍ୟୁମ୍ ବଟନ୍ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଦ୍ୱାରା ଏକ ଆକ୍ସେସବିଲିଟି ଫିଚର୍ ଆରମ୍ଭ ହେବ।"
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index fd3e638cb1b828ee316ea6624c3be94bc3358f8e..9faa33785f1f2165c6027a65072ffdb939fc3edd 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -202,10 +202,8 @@
"%s ਵੱਲੋਂ ਪ੍ਰਿੰਟ ਕਰਨਾ ਬੰਦ ਕੀਤਾ ਗਿਆ।""ਆਪਣਾ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਚਾਲੂ ਕਰੋ""ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਆਪਣਾ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਚਾਲੂ ਨਹੀਂ ਕਰਦੇ ਤੁਹਾਡੀਆਂ ਨਿੱਜੀ ਐਪਾਂ ਬਲਾਕ ਰਹਿੰਦੀਆਂ ਹਨ"
-
-
-
-
+ "%1$s ਨੂੰ %2$s ਵਜੇ ਨਿੱਜੀ ਐਪਾਂ ਨੂੰ ਬਲਾਕ ਕਰ ਦਿੱਤਾ ਜਾਵੇਗਾ। ਤੁਹਾਡਾ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਨੂੰ %3$d ਦਿਨਾਂ ਤੋਂ ਵੱਧ ਸਮੇਂ ਤੱਕ ਬੰਦ ਰਹਿਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦਾ।"
+ "ਚਾਲੂ ਕਰੋ""ਮੈਂ""ਟੈਬਲੈੱਟ ਵਿਕਲਪ""Android TV ਦੇ ਵਿਕਲਪ"
@@ -1621,7 +1619,6 @@
"ਤੁਸੀਂ %1$d ਵਾਰ ਆਪਣਾ ਅਣਲਾਕ ਪੈਟਰਨ ਗਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। %2$d ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਇੱਕ ਈਮੇਲ ਖਾਤਾ ਵਰਤਦੇ ਹੋਏ ਆਪਣਾ ਫ਼ੋਨ ਅਣਲਾਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n %3$d ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"" — ""ਹਟਾਓ"
- "%1$s ਤੋਂ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਸ਼ੁਰੂ ਕੀਤੀ ਗਈ ਫੋਰਗ੍ਰਾਊਂਡ ਸੇਵਾ ਲਈ ਭਵਿੱਖੀ R ਬਿਲਡ ਵਿੱਚ \'ਵਰਤੋਂ ਵਿੱਚ ਹੋਣ \'ਤੇ ਇਜਾਜ਼ਤ\' ਵਿਸ਼ੇਸ਼ਤਾ ਨਹੀਂ ਹੋਵੇਗੀ। ਕਿਰਪਾ ਕਰਕੇ go/r-bg-fgs-restriction ਦੇਖੋ ਅਤੇ ਬੱਗ ਰਿਪੋਰਟ ਫ਼ਾਈਲ ਕਰੋ।""ਕੀ ਵੌਲਿਊਮ ਸਿਫ਼ਾਰਸ਼ ਕੀਤੇ ਪੱਧਰ ਤੋਂ ਵਧਾਉਣੀ ਹੈ?\n\nਲੰਮੇ ਸਮੇਂ ਤੱਕ ਉੱਚ ਵੌਲਿਊਮ ਤੇ ਸੁਣਨ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ।""ਕੀ ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਵਰਤਣਾ ਹੈ?""ਸ਼ਾਰਟਕੱਟ ਚਾਲੂ ਹੋਣ \'ਤੇ, ਕਿਸੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ ਬਟਨਾਂ ਨੂੰ 3 ਸਕਿੰਟ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ।"
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 33104c791190a3f63c27b00ca8752b000348821f..254f1d04be87cddf780f69e3c06fd6cecf922945 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -206,10 +206,8 @@
"Drukowanie wyłączone przez: %s.""Włącz profil do pracy""Zablokowano aplikacje osobiste do czasu włączenia profilu do pracy"
-
-
-
-
+ "Aplikacje osobiste zostaną zablokowane %1$s o %2$s. Administrator IT nie pozwala na wyłączenie profilu służbowego na dłużej niż %3$d dni."
+ "Włącz""Ja""Opcje tabletu""Opcje Androida TV"
@@ -1170,10 +1168,10 @@
"Otwórz w aplikacji""Otwórz w aplikacji %1$s""Otwórz"
- "Otwieraj linki z: %1$s w"
+ "Otwieraj linki z %1$s w""Otwieraj linki w""Otwieraj linki w aplikacji %1$s"
- "Otwieraj linki z: %1$s w aplikacji %2$s"
+ "Otwieraj linki z %1$s w aplikacji %2$s""Udziel uprawnień""Edytuj w aplikacji""Edytuj w aplikacji %1$s"
@@ -1665,7 +1663,6 @@
"Po raz %1$d nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych %2$d nieudanych próbach konieczne będzie odblokowanie telefonu przy użyciu danych logowania na konto Google.\n\n Spróbuj ponownie za %3$d s."" – ""Usuń"
- "Uruchomiona w tle usługa działająca w pierwszym planie z pakietu %1$s nie będzie miała uprawnień obowiązujących podczas używania w przyszłych kompilacjach R. Zapoznaj się z ograniczeniem go/r-bg-fgs-restriction i zgłoś błąd.""Zwiększyć głośność ponad zalecany poziom?\n\nSłuchanie głośno przez długi czas może uszkodzić Twój słuch.""Użyć skrótu do ułatwień dostępu?""Gdy skrót jest włączony, jednoczesne naciskanie przez trzy sekundy obu przycisków głośności uruchamia funkcję ułatwień dostępu."
@@ -1689,7 +1686,7 @@
"Odmów""Wybierz funkcję, aby zacząć z niej korzystać:""Wybierz funkcje, których chcesz używać z przyciskiem ułatwień dostępu"
- "Wybierz funkcje, których chcesz używać w przypadku skrótu z klawiszami głośności"
+ "Wybierz funkcje, do których chcesz używać skrótu z klawiszami głośności""Usługa %s została wyłączona""Edytuj skróty""OK"
@@ -2125,18 +2122,18 @@
"Służbowe""Widok osobisty""Widok służbowy"
- "Nie można udostępnić aplikacjom do pracy"
- "Administrator IT nie pozwala na udostępnianie tej zawartości w profilu do pracy"
- "Nie można otworzyć w aplikacjach do pracy"
- "Administrator IT nie pozwala na otwieranie tej zawartości w profilu do pracy"
+ "Nie można udostępnić aplikacjom służbowym"
+ "Administrator IT nie pozwala na udostępnianie tej zawartości w profilu służbowym"
+ "Nie można otworzyć w aplikacjach służbowych"
+ "Administrator IT nie pozwala na otwieranie tej zawartości w profilu służbowym""Nie można udostępnić aplikacjom osobistym""Administrator IT nie pozwala na udostępnianie tej zawartości w profilu osobistym""Nie można otworzyć w aplikacjach osobistych""Administrator IT nie pozwala na otwieranie tej zawartości w profilu osobistym"
- "Działanie profilu do pracy jest wstrzymane"
+ "Działanie profilu służbowego jest wstrzymane""Włącz"
- "Brak aplikacji do pracy, które obsługują tę zawartość"
- "Brak aplikacji do pracy, które mogą otworzyć tę zawartość"
+ "Brak aplikacji służbowych, które obsługują tę zawartość"
+ "Brak aplikacji służbowych, które mogą otworzyć tę zawartość""Brak aplikacji osobistych, które obsługują tę zawartość""Brak aplikacji osobistych, które mogą otworzyć tę zawartość""Kod PIN do karty SIM odblokowujący sieć"
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 25f8633b44ad5eb28c73b6117398841706632ced..bea530000b225caa6b9cf6d72b89d8effcc53388 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -56,8 +56,8 @@
"IMEI""MEID"
- "ID do chamador de entrada"
- "ID do chamador de saída"
+ "Identificador de chamadas recebidas"
+ "Identificador de chamadas realizadas""ID de linha conectada""Restrição de ID de linha conectada""Encaminhamento de chamada"
@@ -71,12 +71,12 @@
"Rejeição das chamadas indesejadas""Chamando número de entrega""Não perturbe"
- "O ID do chamador assume o padrão de restrito. Próxima chamada: Restrita"
- "O ID do chamador assume o padrão de restrito. Próxima chamada: Não restrita"
- "O ID do chamador assume o padrão de não restrito. Próxima chamada: Restrita"
- "O ID do chamador assume o padrão de não restrito. Próxima chamada: Não restrita"
+ "O identificador de chamadas assume o padrão de restrito. Próxima chamada: Restrita"
+ "O identificador de chamadas assume o padrão de restrito. Próxima chamada: Não restrita"
+ "O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Restrita"
+ "O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Não restrita""O serviço não foi habilitado."
- "Não é possível alterar a configuração de identificação de chamadas."
+ "Não é possível alterar a configuração do identificador de chamadas.""Nenhum serviço móvel de dados""Chamadas de emergência indisponíveis""Sem serviço de voz"
@@ -202,10 +202,8 @@
"Impressão desativada por %s.""Ativar perfil de trabalho""Seus apps pessoais ficarão bloqueados até você ativar o perfil de trabalho"
-
-
-
-
+ "Os apps pessoais serão bloqueados em %1$s, %2$s. Seu administrador de TI não permite que o perfil de trabalho fique desativado por mais de %3$d dias."
+ "Ativar""Eu""Opções do tablet""Opções do Android TV"
@@ -1130,10 +1128,10 @@
"Abrir com""Abrir com %1$s""Abrir"
- "Abrir links do domínio %1$s com"
+ "Abrir links do domínio %1$s usando""Abrir links com""Abrir links com %1$s"
- "Abrir links do domínio %1$s com %2$s"
+ "Abrir links do domínio %1$s usando %2$s""Conceder acesso""Editar com""Editar com %1$s"
@@ -1313,7 +1311,7 @@
"Selecione para desativar a depuração USB.""Depuração por Wi-Fi conectada""Toque para desativar a depuração por Wi-Fi"
- "Selecione para desativar a depuração sem fio."
+ "Selecione para desativar a depuração por Wi-Fi.""Modo Arcabouço de testes ativado""Realize uma redefinição para configuração original para desativar o modo Arcabouço de testes.""Console serial ativado"
@@ -1621,7 +1619,6 @@
"Você desenhou sua sequência de desbloqueio incorretamente %1$d vezes. Se fizer mais %2$d tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em %3$d segundos."" — ""Remover"
- "O serviço em primeiro plano iniciado em segundo plano por %1$s não receberá uma permissão durante o uso em futuras versões R. Consulte go/r-bg-fgs-restriction e crie um relatório de bug.""Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição.""Usar atalho de Acessibilidade?""Quando o atalho estiver ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade."
@@ -1654,7 +1651,7 @@
"Inversão de cores""Correção de cor""Teclas de volume pressionadas. Serviço %1$s ativado."
- "Teclas de volume pressionadas. Serviço %1$s desativado."
+ "Teclas de volume pressionadas. %1$s desativado.""Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o %1$s""Escolha um recurso a ser usado quando você toca no botão de acessibilidade:""Escolha um recurso para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com dois dedos):"
@@ -2059,12 +2056,12 @@
"Visualização de trabalho""Não é possível compartilhar este conteúdo com apps de trabalho""Seu administrador de TI não permite que você compartilhe este conteúdo com apps no seu perfil de trabalho"
- "Não é possível abrir com apps de trabalho"
- "Seu administrador de TI não permite que você abra este conteúdo com apps no seu perfil de trabalho"
+ "Não é possível abrir usando apps de trabalho"
+ "Seu administrador de TI não permite que você abra este conteúdo usando apps do seu perfil de trabalho""Não é possível compartilhar este conteúdo com apps pessoais""Seu administrador de TI não permite que você compartilhe este conteúdo com apps no seu perfil pessoal""Não é possível abrir este conteúdo com apps pessoais"
- "Seu administrador de TI não permite que você abra este conteúdo com apps no seu perfil pessoal"
+ "Seu administrador de TI não permite que você abra este conteúdo usando apps do seu perfil pessoal""O perfil de trabalho está pausado""Ativar""Apps de trabalho não são compatíveis com este conteúdo"
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5bc4fbdaeda896c5b317878054739e821b612d7d..dbd0d452abdd21135ab5fe93bfbb571cd04008be 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -202,10 +202,8 @@
"Impressão desativada por %s.""Ative o perfil de trabalho""As suas apps pessoais estão bloqueadas até ativar o seu perfil de trabalho."
-
-
-
-
+ "As apps pessoais serão bloqueadas a %1$s à(s) %2$s. O seu administrador de TI não permite que o seu perfil de trabalho fique desativado mais de %3$d dias."
+ "Ativar""Eu""Opções do tablet""Opções do Android TV"
@@ -239,7 +237,7 @@
"Opções do telefone""Bloqueio de ecrã""Desligar"
- "Ligar/desligar"
+ "Ligar""Reiniciar""Emergência""Relatório de erros"
@@ -266,7 +264,7 @@
"Definições""Assistência""Assist. de voz"
- "Bloqueio"
+ "Bloquear""999+""Nova notificação""Teclado virtual"
@@ -1131,7 +1129,7 @@
"Abrir com %1$s""Abrir""Abra os links de %1$s com:"
- "Abra os links com:"
+ "Abrir os links com:""Abra os links com a app %1$s""Abra os links de %1$s com a app %2$s""Conceder acesso"
@@ -1335,7 +1333,7 @@
"Toque para selecionar o idioma e o esquema"" ABCDEFGHIJKLMNOPQRSTUVWXYZ"" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "Sobrepor a outras aplicações"
+ "Sobrepor a outras apps""A app %s sobrepõe-se a outras aplicações""O %s sobrepõe-se a outras app""Se não pretende que a app %s utilize esta funcionalidade, toque para abrir as definições e desative-a."
@@ -1621,7 +1619,6 @@
"Desenhou o padrão de desbloqueio incorretamente %1$d vezes. Depois de mais %2$d tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telemóvel através de uma conta de email.\n\n Tente novamente dentro de %3$d segundos."" - ""Remover"
- "O serviço em primeiro plano iniciado em segundo plano de %1$s não terá a autorização durante a utilização em compilações R futuras. Aceda a go/r-bg-fgs-restriction e envie um relatório de erros.""Aumentar o volume acima do nível recomendado?\n\nOuvir com um volume elevado durante longos períodos poderá ser prejudicial para a sua audição.""Pretende utilizar o atalho de acessibilidade?""Quando o atalho está ativado, premir ambos os botões de volume durante 3 segundos inicia uma funcionalidade de acessibilidade."
@@ -1645,7 +1642,7 @@
"Recusar""Toque numa funcionalidade para começar a utilizá-la:""Escolha funcionalidades para utilizar com o botão Acessibilidade"
- "Escolha funcionalidades para utilizar com o atalho das teclas de volume"
+ "Escolha funcionalidades para usar com o atalho das teclas de volume""O serviço %s foi desativado.""Editar atalhos""Concluído"
@@ -1654,7 +1651,7 @@
"Inversão de cores""Correção da cor""Teclas do volume premidas. Serviço %1$s ativado."
- "Teclas do volume premidas. Serviço %1$s desativado."
+ "Teclas de volume premidas. Serviço %1$s desativado.""Prima sem soltar as teclas de volume durante três segundos para utilizar o serviço %1$s.""Escolha uma funcionalidade para utilizar quando tocar no botão Acessibilidade:""Escolha a funcionalidade a utilizar com o gesto de acessibilidade (deslize rapidamente com dois dedos para cima a partir da parte inferior do ecrã):"
@@ -2065,7 +2062,7 @@
"O seu administrador de TI não lhe permite partilhar este conteúdo com apps no seu perfil pessoal.""Não é possível abrir este conteúdo com apps pessoais""O seu administrador de TI não lhe permite abrir este conteúdo com apps no seu perfil pessoal."
- "Perfil de trabalho em pausa."
+ "Perfil de trabalho em pausa""Ativar""Este conteúdo não é suportado por nenhuma app de trabalho.""Este conteúdo não pode ser aberto por nenhuma app de trabalho."
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 25f8633b44ad5eb28c73b6117398841706632ced..bea530000b225caa6b9cf6d72b89d8effcc53388 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -56,8 +56,8 @@
"IMEI""MEID"
- "ID do chamador de entrada"
- "ID do chamador de saída"
+ "Identificador de chamadas recebidas"
+ "Identificador de chamadas realizadas""ID de linha conectada""Restrição de ID de linha conectada""Encaminhamento de chamada"
@@ -71,12 +71,12 @@
"Rejeição das chamadas indesejadas""Chamando número de entrega""Não perturbe"
- "O ID do chamador assume o padrão de restrito. Próxima chamada: Restrita"
- "O ID do chamador assume o padrão de restrito. Próxima chamada: Não restrita"
- "O ID do chamador assume o padrão de não restrito. Próxima chamada: Restrita"
- "O ID do chamador assume o padrão de não restrito. Próxima chamada: Não restrita"
+ "O identificador de chamadas assume o padrão de restrito. Próxima chamada: Restrita"
+ "O identificador de chamadas assume o padrão de restrito. Próxima chamada: Não restrita"
+ "O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Restrita"
+ "O identificador de chamadas assume o padrão de não restrito. Próxima chamada: Não restrita""O serviço não foi habilitado."
- "Não é possível alterar a configuração de identificação de chamadas."
+ "Não é possível alterar a configuração do identificador de chamadas.""Nenhum serviço móvel de dados""Chamadas de emergência indisponíveis""Sem serviço de voz"
@@ -202,10 +202,8 @@
"Impressão desativada por %s.""Ativar perfil de trabalho""Seus apps pessoais ficarão bloqueados até você ativar o perfil de trabalho"
-
-
-
-
+ "Os apps pessoais serão bloqueados em %1$s, %2$s. Seu administrador de TI não permite que o perfil de trabalho fique desativado por mais de %3$d dias."
+ "Ativar""Eu""Opções do tablet""Opções do Android TV"
@@ -1130,10 +1128,10 @@
"Abrir com""Abrir com %1$s""Abrir"
- "Abrir links do domínio %1$s com"
+ "Abrir links do domínio %1$s usando""Abrir links com""Abrir links com %1$s"
- "Abrir links do domínio %1$s com %2$s"
+ "Abrir links do domínio %1$s usando %2$s""Conceder acesso""Editar com""Editar com %1$s"
@@ -1313,7 +1311,7 @@
"Selecione para desativar a depuração USB.""Depuração por Wi-Fi conectada""Toque para desativar a depuração por Wi-Fi"
- "Selecione para desativar a depuração sem fio."
+ "Selecione para desativar a depuração por Wi-Fi.""Modo Arcabouço de testes ativado""Realize uma redefinição para configuração original para desativar o modo Arcabouço de testes.""Console serial ativado"
@@ -1621,7 +1619,6 @@
"Você desenhou sua sequência de desbloqueio incorretamente %1$d vezes. Se fizer mais %2$d tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em %3$d segundos."" — ""Remover"
- "O serviço em primeiro plano iniciado em segundo plano por %1$s não receberá uma permissão durante o uso em futuras versões R. Consulte go/r-bg-fgs-restriction e crie um relatório de bug.""Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição.""Usar atalho de Acessibilidade?""Quando o atalho estiver ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade."
@@ -1654,7 +1651,7 @@
"Inversão de cores""Correção de cor""Teclas de volume pressionadas. Serviço %1$s ativado."
- "Teclas de volume pressionadas. Serviço %1$s desativado."
+ "Teclas de volume pressionadas. %1$s desativado.""Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o %1$s""Escolha um recurso a ser usado quando você toca no botão de acessibilidade:""Escolha um recurso para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com dois dedos):"
@@ -2059,12 +2056,12 @@
"Visualização de trabalho""Não é possível compartilhar este conteúdo com apps de trabalho""Seu administrador de TI não permite que você compartilhe este conteúdo com apps no seu perfil de trabalho"
- "Não é possível abrir com apps de trabalho"
- "Seu administrador de TI não permite que você abra este conteúdo com apps no seu perfil de trabalho"
+ "Não é possível abrir usando apps de trabalho"
+ "Seu administrador de TI não permite que você abra este conteúdo usando apps do seu perfil de trabalho""Não é possível compartilhar este conteúdo com apps pessoais""Seu administrador de TI não permite que você compartilhe este conteúdo com apps no seu perfil pessoal""Não é possível abrir este conteúdo com apps pessoais"
- "Seu administrador de TI não permite que você abra este conteúdo com apps no seu perfil pessoal"
+ "Seu administrador de TI não permite que você abra este conteúdo usando apps do seu perfil pessoal""O perfil de trabalho está pausado""Ativar""Apps de trabalho não são compatíveis com este conteúdo"
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 891c6eda1c234e32d6bb25991ce9319591ea720b..4c5fbe6cec6f538142f7cb2721a659067f209a15 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -204,10 +204,8 @@
"Printare dezactivată de %s.""Activați profilul de serviciu""Aplicațiile personale sunt blocate până când activați profilul de serviciu"
-
-
-
-
+ "Aplicațiile personale vor fi blocate pe %1$s, la %2$s. Administratorul IT nu permite ca profilul de serviciu să fie dezactivat mai mult de %3$d zile."
+ "Activați""Eu""Opțiuni tablet PC""Opțiuni pentru Android TV"
@@ -1643,7 +1641,6 @@
"Ați desenat incorect modelul pentru deblocare de %1$d ori. După încă %2$d încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste %3$d secunde."" — ""Eliminați"
- "Serviciul în prim-plan inițializat în fundal din %1$s nu va avea permisiunea în timpul utilizării în versiunile R viitoare. Consultați go/r-bg-fgs-restriction și trimiteți un raport de eroare.""Ridicați volumul mai sus de nivelul recomandat?\n\nAscultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul.""Utilizați comanda rapidă pentru accesibilitate?""Atunci când comanda rapidă este activată, dacă apăsați ambele butoane de volum timp de trei secunde, veți lansa o funcție de accesibilitate."
@@ -1667,7 +1664,7 @@
"Refuzați""Atingeți o funcție ca să începeți să o folosiți:""Alegeți funcțiile pe care să le folosiți cu butonul de accesibilitate"
- "Alegeți funcțiile pe care să le folosiți cu comanda rapidă pentru butonul de volum"
+ "Alegeți funcțiile pentru comanda rapidă a butonului de volum""%s a fost dezactivat""Editați comenzile rapide""Gata"
@@ -2092,7 +2089,7 @@
"Afișarea conținutului personal""Afișarea conținutului de lucru""Nu se poate trimite către aplicații pentru lucru"
- "Administratorul IT nu vă permite să trimiteți acest conținut cu aplicațiile din profilul de serviciu"
+ "Administratorul IT nu vă permite să trimiteți acest conținut către aplicațiile din profilul de serviciu""Nu se poate deschide cu aplicații pentru lucru""Administratorul IT nu vă permite să deschideți acest conținut cu aplicațiile din profilul de serviciu""Nu se poate trimite către aplicații personale"
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 58806e99d56792de10387aa9f66fb88a44601f16..6d348d0eb7a1f75d3b8ba8a3d0153c61f6e796be 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -63,7 +63,7 @@
"Идентификатор подключенной линии""Ограничение идентификатора подключенной линии""Переадресация вызовов"
- "Параллельный вызов"
+ "Ожидание вызова""Запрет вызовов""Смена пароля""Смена PIN"
@@ -206,10 +206,8 @@
"Функция печати отключена приложением \"%s\"""Включите рабочий профиль""Личные приложения будут заблокированы, пока вы не включите рабочий профиль."
-
-
-
-
+ "Личные приложения будут заблокированы %1$s в %2$s. Системный администратор запретил отключать рабочий профиль более чем на %3$d дн."
+ "Включить""Я""Настройки планшетного ПК""Настройки Android TV"
@@ -244,7 +242,7 @@
"Блокировка экрана""Выключить""Питание"
- "Перезапуск"
+ "Перезапустить""Экстренный вызов""Отчет об ошибке""Закончить сеанс"
@@ -1351,7 +1349,7 @@
"Отладка по USB разрешена""Нажмите, чтобы отключить отладку по USB.""Нажмите, чтобы отключить отладку по USB."
- "Отладка по Wi-Fi активна"
+ "Отладка по Wi-Fi включена""Нажмите, чтобы отключить отладку по Wi-Fi.""Нажмите, чтобы отключить отладку по Wi-Fi.""Тестовый режим включен"
@@ -1665,12 +1663,11 @@
"Вы %1$d раз неверно указали графический ключ. После %2$d неверных попыток для разблокировки телефона потребуется войти в аккаунт Google.\n\nПовтор через %3$d сек."" – ""Удалить"
- "Службы из пакета %1$s, переведенные из фонового режима в активный, не будут получать разрешение while-in-use в будущих сборках на языке R. Перейдите на страницу go/r-bg-fgs-restriction и отправьте отчет об ошибке.""Установить громкость выше рекомендуемого уровня?\n\nВоздействие громкого звука в течение долгого времени может привести к повреждению слуха.""Использовать быстрое включение?""Чтобы использовать функцию специальных возможностей, когда она включена, нажмите и удерживайте обе кнопки регулировки громкости в течение трех секунд.""Включить специальные возможности?"
- "Чтобы включить специальные возможности, нажмите обе кнопки регулировки громкости на несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nТекущие функции:\n%1$s\nЧтобы изменить выбранные функции, перейдите в настройки и нажмите \"Специальные возможности\"."
+ "Чтобы включить специальные возможности, нажмите обе кнопки регулировки громкости и удерживайте несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nТекущие функции:\n%1$s\nЧтобы изменить выбранные функции, перейдите в настройки и нажмите \"Специальные возможности\"."" • %1$s\n""Включить функцию \"%1$s\"?""Чтобы включить функцию \"%1$s\", нажмите обе кнопки регулировки громкости на несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nЧтобы назначить это сочетание клавиш другой функции, перейдите в настройки и выберите \"Специальные возможности\"."
@@ -2133,7 +2130,7 @@
"Системный администратор запретил делиться этим контентом с приложениями из личного профиля.""Этот контент нельзя открывать в личных приложениях""Системный администратор запретил открывать этот контент, используя приложения из личного профиля."
- "Рабочий профиль приостановлен."
+ "Действие рабочего профиля приостановлено.""Включить""Нет рабочих приложений, поддерживающих этот контент.""Нет рабочих приложений, с помощью которых можно открыть этот контент."
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index fecfa1e784df86519fe81dcfb8885ef2bb8421f3..b477be89b6dc0dd65c453f5c8e50926db9e18ce2 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -202,10 +202,8 @@
"%s විසින් මුද්රණය කිරීම අබල කර ඇත.""ඔබේ කාර්යාල පැතිකඩ ඔන් කරන්න""ඔබ ඔබගේ කාර්යාල පැතිකඩ ක්රියාත්මක කරන තෙක් ඔබගේ පෞද්ගලික යෙදුම් අවහිර කර ඇත"
-
-
-
-
+ "පෞද්ගලික යෙදුම් %1$s දින %2$sට අවහිර වනු ඇත. ඔබේ IT පරිපාලක ඔබේ කාර්යාල පැතිකඩ දින %3$dකට වඩා ඉවත් කර තැබීමට ඉඩ නොදේ."
+ "ක්රියාත්මක කරන්න""මම""ටැබ්ලට විකල්ප""Android TV විකල්ප"
@@ -1621,7 +1619,6 @@
"ඔබ වැරදියට %1$d වතාවක් ඔබගේ අගුළු හැරීමේ රටාව ඇඳ ඇත. අසාර්ථක උත්සහ කිරීම් %2$d න් පසුව, ඔබගේ ඊ-තැපැල් ලිපිනය භාවිතයෙන් ඔබගේ දුරකථනය අගුළු හැරීමට ඔබගෙන් අසයි.\n\n තත්පර %3$d න් පසුව නැවත උත්සහ කරන්න."" — ""ඉවත් කරන්න"
- "%1$s වෙතින් පසුබිම ආරම්භ කරන ලද පෙරබිම් සේවාව අනාගත R තැනුම්වලදී භාවිතයේ අවසරය නැත. කරුණාකර go/r-bg-fgs-අවහිරතාව බලා දෝෂ වාර්තාවක් ගොනු කරන්න.""නිර්දේශිතයි මට්ටමට වඩා ශබ්දය වැඩිද?\n\nදිගු කාලයක් සඳහා ඉහළ ශබ්දයක් ඇසීමෙන් ඇතැම් විට ඔබගේ ඇසීමට හානි විය හැක.""ප්රවේශ්යතා කෙටිමඟ භාවිතා කරන්නද?""කෙටිමග ක්රියාත්මක විට, හඬ පරිමා බොත්තම් දෙකම තත්පර 3ක් තිස්සේ එබීමෙන් ප්රවේශ්යතා විශේෂාංගය ආරම්භ වනු ඇත."
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index c218f28133d901625fa1c709d3d2a20e47a6ffc2..11c99a1f57d446c41a5b0d372fd5cb8e7fc1b236 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -206,10 +206,8 @@
"Tlač zakázala aplikácia %s.""Zapnite svoj pracovný profil""Vaše osobné aplikácie sú zablokované, dokým nezapnete svoj pracovný profil"
-
-
-
-
+ "Osobné aplikácie budú %1$s o %2$s zablokované. Váš správca IT nepovoľuje, aby váš pracovný profil bol vypnutý viac ako %3$d d."
+ "Zapnúť""Ja""Možnosti tabletu""Možnosti zariadenia Android TV"
@@ -243,13 +241,13 @@
"Možnosti telefónu""Zámka obrazovky""Vypnúť"
- "Vypnutie"
+ "Vypnúť""Reštartovať"
- "Tiesňový režim"
+ "Tieseň""Hlásenie o chybách""Ukončiť reláciu""Snímka obrazovky"
- "Hlásenie chyby"
+ "Nahlásiť chybu""Týmto zhromaždíte informácie o aktuálnom stave zariadenia. Informácie je potom možné odoslať e-mailom, chvíľu však potrvá, kým bude hlásenie chyby pripravené na odoslanie. Prosíme vás preto o trpezlivosť.""Interaktívne nahlásenie""Táto možnosť je vhodná pre väčšinu prípadov. Umožňuje sledovať priebeh nahlásenia, zadávať ďalšie podrobnosti o probléme a vytvárať snímky obrazovky. Môžu byť vynechané niektoré menej používané sekcie, ktorých nahlásenie trvá dlho."
@@ -1170,10 +1168,10 @@
"Otvoriť v aplikácii""Otvoriť v aplikácii %1$s""Otvoriť"
- "Otvárajte odkazy z webu %1$s v prehliadači alebo aplikácii"
- "Otvárajte odkazy v prehliadači"
- "Otvárajte odkazy v prehliadači %1$s"
- "Otvárajte odkazy z webu %1$s v prehliadači %2$s"
+ "Otvárať odkazy %1$s v aplikácii"
+ "Otvárať odkazy v aplikácii"
+ "Otvárať odkazy v aplikácii %1$s"
+ "Otvárať odkazy %1$s v aplikácii %2$s""Udeliť prístup""Upraviť pomocou""Upraviť v aplikácii %1$s"
@@ -1665,12 +1663,11 @@
"%1$d-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po %2$d ďalších neúspešných pokusoch sa zobrazí výzva na odomknutie telefónu pomocou e-mailového účtu.\n\n Skúste to znova o %3$d s."" — ""Odstrániť"
- "Služba na popredí spustená na pozadí z balíka %1$s nebude mať v budúcich zostavách R povolenie Počas používania. Prejdite na go/r-bg-fgs-restriction a odošlite hlásenie chyby.""Zvýšiť hlasitosť nad odporúčanú úroveň?\n\nDlhodobé počúvanie pri vysokej hlasitosti môže poškodiť váš sluch.""Použiť skratku dostupnosti?""Keď je skratka zapnutá, stlačením obidvoch tlačidiel hlasitosti na tri sekundy spustíte funkciu dostupnosti.""Chcete zapnúť funkcie dostupnosti?"
- "Pridržaním oboch klávesov hlasitosti na niekoľko sekúnd zapnete funkcie dostupnosti. Môže sa tým zmeniť spôsob fungovania vášho zariadenia.\n\nAktuálne funkcie:\n%1$s\nVybrané funkcie môžete zmeniť v časti Nastavenia > Dostupnosť."
+ "Pridržaním oboch tlačidiel hlasitosti na niekoľko sekúnd zapnete funkcie dostupnosti. Môže sa tým zmeniť spôsob fungovania vášho zariadenia.\n\nAktuálne funkcie:\n%1$s\nVybrané funkcie môžete zmeniť v časti Nastavenia > Dostupnosť."" • %1$s\n""Chcete zapnúť %1$s?""Pridržaním oboch klávesov hlasitosti na niekoľko sekúnd zapnete funkciu dostupnosti %1$s. Môže sa tým zmeniť spôsob fungovania vášho zariadenia.\n\nTúto skratku môžete zmeniť na inú funkciu v časti Nastavenia > Dostupnosť."
@@ -1697,8 +1694,8 @@
"Použiť skratku""Inverzia farieb""Úprava farieb"
- "Pridržali ste klávesy hlasitosti. Služba %1$s je zapnutá."
- "Pridržali ste klávesy hlasitosti. Služba %1$s je vypnutá."
+ "Pridržali ste tlačidlá hlasitosti. Služba %1$s je zapnutá."
+ "Pridržali ste tlačidlá hlasitosti. Služba %1$s je vypnutá.""Ak chcete používať službu %1$s, pridržte tri sekundy oba klávesy hlasitosti""Klepnutím na tlačidlo dostupnosti vyberte požadovanú funkciu:""Vyberte funkciu, ktorú chcete používať s daným gestom dostupnosti (potiahnutím dvoma prstami z dolnej časti obrazovky nahor):"
@@ -2126,13 +2123,13 @@
"Osobné zobrazenie""Pracovné zobrazenie""Nedá sa zdieľať s pracovnými aplikáciami"
- "Správca IT vám zakázal zdieľať tento obsah pomocou aplikácií v pracovnom profile"
+ "Váš správca IT vám zakázal zdieľať tento obsah pomocou aplikácií v pracovnom profile""Nedá sa otvoriť pomocou pracovných aplikácií"
- "Správca IT vám zakázal otvoriť tento obsah pomocou aplikácií v pracovnom profile"
+ "Váš správca IT vám zakázal otvárať tento obsah pomocou aplikácií v pracovnom profile""Nedá sa zdieľať pomocou osobných aplikácií"
- "Správca IT vám zakázal zdieľať tento obsah pomocou aplikácií v osobnom profile"
+ "Váš správca IT vám zakázal zdieľať tento obsah pomocou aplikácií v osobnom profile""Nedá sa otvoriť pomocou osobných aplikácií"
- "Správca IT vám zakázal otvoriť tento obsah pomocou aplikácií v osobnom profile"
+ "Váš správca IT vám zakázal otvárať tento obsah pomocou aplikácií v osobnom profile""Pracovný profil je pozastavený""Zapnúť""Tento obsah nepodporujú žiadne pracovné aplikácie"
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 1bb856ee16e0ba6bf108ad4541c7ff270b7313b9..e21cc58dfda54633372ecfecdf229d4ab70fa3e6 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -206,10 +206,8 @@
"Tiskanje je onemogočil pravilnik %s.""Vklopite delovni profil""Osebne aplikacije so blokirane, dokler ne vklopite delovnega profila"
-
-
-
-
+ "Osebne aplikacije bodo blokirane %1$s ob %2$s. Skrbnik za IT ne dovoli, da bi bil delovni profil izklopljen več kot toliko dni: %3$d."
+ "Vklopi""Jaz""Možnosti tabličnega računalnika""Možnosti naprave Android TV"
@@ -1170,7 +1168,7 @@
"Odpiranje z aplikacijo""Odpiranje z aplikacijo %1$s""Odpiranje"
- "Odpiranje povezav %1$s z"
+ "Odpiranje povezav %1$s z aplikacijo""Odpiranje povezav z""Odpiranje povezav z aplikacijo %1$s""Odpiranje povezav %1$s z aplikacijo %2$s"
@@ -1665,7 +1663,6 @@
"Vzorec za odklepanje ste %1$d-krat napačno vnesli. Po nadaljnjih %2$d neuspešnih poskusih boste pozvani, da odklenete telefon z Googlovimi podatki za prijavo.\n\nPoskusite znova čez %3$d s."" – ""Odstrani"
- "V prihodnjih različicah R storitev v ospredju z zagonom iz ozadja iz paketa %1$s ne bo imela dovoljenja med uporabo aplikacije. Oglejte si go/r-bg-fgs-restriction in pošljite poročilo o napakah.""Ali želite povečati glasnost nad priporočeno raven?\n\nDolgotrajno poslušanje pri veliki glasnosti lahko poškoduje sluh.""Želite uporabljati bližnjico funkcij za ljudi s posebnimi potrebami?""Ko je bližnjica vklopljena, pritisnite gumba za glasnost in ju pridržite tri sekunde, če želite zagnati funkcijo za ljudi s posebnimi potrebami."
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index bea3b724591d2c685e4c3b9251c3eea1bb28ac65..1e5c42c772007c066990b686ae2cc4e806082805 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -202,10 +202,8 @@
"Printimi është çaktivizuar nga %s.""Aktivizo profilin e punës""Aplikacionet e tua personale janë bllokuar derisa të aktivizosh profilin tënd të punës"
-
-
-
-
+ "Aplikacionet personale do të bllokohen më %1$s në %2$s. Administratori yt i teknologjisë së informacionit nuk lejon që profili yt i punës të qëndrojë joaktiv për më shumë se %3$d ditë."
+ "Aktivizo""Unë""Opsionet e tabletit""Opsionet e Android TV"
@@ -1161,7 +1159,7 @@
"%1$s vazhdon të ndalojë""%1$s vazhdon të ndalojë""Hap përsëri aplikacionin"
- "Dërgo koment"
+ "Dërgo reagim""Mbyll""Vendose në heshtje deri kur të riniset pajisja""Prit!"
@@ -1313,7 +1311,7 @@
"Përzgjidhe për të çaktivizuar korrigjimin e gabimeve të USB-së""Korrigjimi përmes Wi-Fi është lidhur""Trokit për të çaktivizuar korrigjimin përmes Wi-Fi"
- "Zgjidh për të çaktivizuar korrigjimin përmes Wi-Fi"
+ "Zgjidh për të çaktivizuar korrigjimin përmes Wi-Fi.""Modaliteti i lidhjes së testimit është aktivizuar""Kryej një rivendosje në cilësimet e fabrikës për të çaktivizuar \"Modalitetin e lidhjes së testimit\".""Paneli komandues i serisë është aktivizuar"
@@ -1621,7 +1619,6 @@
"Ke vizatuar %1$d herë pa sukses motivin tënd. Pas %2$d tentativave të tjera të pasuksesshme, do të të duhet ta shkyçësh telefonin duke përdorur një llogari mail-i.\n\n Provo sërish për %3$d sekonda."" - ""Hiq"
- "Shërbimi në plan të parë i nisur në sfond nga %1$s nuk do të ketë lejen e nevojshme gjatë përdorimit në ndërtimet e ardhshme R. Shiko go/r-bg-fgs-restriction dhe dërgo një raport të defekteve në kod.""Të ngrihet volumi mbi nivelin e rekomanduar?\n\nDëgjimi me volum të lartë për periudha të gjata mund të dëmtojë dëgjimin.""Të përdoret shkurtorja e qasshmërisë?""Kur shkurtorja është e aktivizuar, shtypja e të dy butonave për 3 sekonda do të nisë një funksion qasshmërie."
@@ -1654,7 +1651,7 @@
"Kthimi i ngjyrës""Korrigjimi i ngjyrës""Tastet e volumit të mbajtura shtypur. %1$s i aktivizuar."
- "Tastet e volumit të mbajtura shtypur. %1$s i çaktivizuar."
+ "Tastet e volumit të mbajtura shtypur. U çaktivizua \"%1$s\".""Shtyp dhe mbaj shtypur të dy butonat e volumit për tre sekonda për të përdorur %1$s""Zgjidh një funksion për ta përdorur kur troket butonin e qasshmërisë:""Zgjidh një veçori për ta përdorur me gjestin e qasshmërisë (rrëshqit shpejt lart nga fundi i ekranit me dy gishta):"
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 46b2b6c0129a45d5c214a9d10db26aee4b075a2f..6ca87a1fb926716afb35901e4d351e9ca36aab29 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -204,10 +204,8 @@
"Штампање је онемогућила апликација %s.""Укључите пословни профил""Личне апликације су блокиране док не укључите пословни профил"
-
-
-
-
+ "Личне апликације ће бити блокиране: %1$s у %2$s. ИТ администратор не дозвољава да пословни профил буде искључен дуже од %3$d дана."
+ "Укључи""Ја""Опције за таблет""Опције Android TV-а"
@@ -1151,7 +1149,7 @@
"Отворите помоћу апликације %1$s""Отвори""Отварајте %1$s линкове помоћу"
- "Отваратеј линкове помоћу"
+ "Отварај линкове помоћу""Отварајте линкове помоћу апликације %1$s""Отварајте %1$s линкове помоћу апликације %2$s""Дозволи приступ"
@@ -1643,7 +1641,6 @@
"Нацртали сте шаблон за откључавање нетачно %1$d пута. После још %2$d неуспешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу налога е-поште.\n\nПробајте поново за %3$d секунде/и."" – ""Уклони"
- "Услуга у првом плану са %1$s која је покренута у позадини неће имати дозволу током коришћења у будућим R верзијама. Посетите go/r-bg-fgs-restriction и пошаљите извештај о грешци.""Желите да појачате звук изнад препорученог нивоа?\n\nСлушање гласне музике дуже време може да вам оштети слух.""Желите ли да користите пречицу за приступачност?""Када је пречица укључена, притисните оба дугмета за јачину звука да бисте покренули функцију приступачности."
@@ -1667,7 +1664,7 @@
"Одбиј""Додирните неку функцију да бисте почели да је користите:""Одаберите функције које ћете користити са дугметом Приступачност"
- "Одаберите функције које ћете користити са тастером јачине звука као пречицом"
+ "Одаберите функције за пречицу тастером јачине звука""Услуга %s је искључена""Измените пречице""Готово"
@@ -1675,8 +1672,8 @@
"Користи пречицу""Инверзија боја""Корекција боја"
- "Задржали сте тастере за јачину звука. Услуга %1$s је укључена."
- "Задржали сте тастере за јачину звука. Услуга %1$s је искључена."
+ "Држали сте тастере за јачину звука. Услуга %1$s је укључена."
+ "Држали сте тастере за јачину звука. Услуга %1$s је искључена.""Притисните и задржите оба тастера за јачину звука три секунде да бисте користили %1$s""Изаберите функцију која ће се користити када додирнете дугме Приступачност:""Одаберите функцију која ће се користити помоћу покрета за приступачност (помоћу два прста превуците нагоре од дна екрана):"
@@ -2101,7 +2098,7 @@
"ИТ администратор вам не дозвољава да отворите овај садржај помоћу апликација на личном профилу""Пословни профил је паузиран""Укључи"
- "Ниједна апликација за посао не може да подржава овај садржај"
+ "Ниједна апликација за посао не подржава овај садржај""Ниједна апликација за посао не може да отвори овај садржај""Ниједна лична апликација не може да подржава овај садржај""Ниједна лична апликација не може да отвори овај садржај"
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index d9ee54e202b8a6afb0d62cf480c01db5953d44e4..8bbf7756b0d73538f39463890aee6134463e235e 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -202,10 +202,8 @@
"Utskrift har inaktiverats av %s.""Aktivera jobbprofilen""Privata appar blockeras tills du aktiverar jobbprofilen"
-
-
-
-
+ "Privata appar blockeras den %1$s kl. %2$s. IT-administratören tillåter inte att din jobbprofil är inaktiverad i mer än %3$d dagar."
+ "Aktivera""Jag""Alternativ för surfplattan""Alternativ för Android TV"
@@ -1621,12 +1619,11 @@
"Du har ritat ditt grafiska lösenord fel %1$d gånger. Efter ytterligare %2$d försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto.\n\n Försök igen om %3$d sekunder."" – ""Ta bort"
- "Förgrundstjänsten från %1$s som startades i bakgrunden får inte behörighet som gäller vid användning i framtida R-versioner. Besök go/r-bg-fgs-restriction och skicka en felrapport.""Vill du höja volymen över den rekommenderade nivån?\n\nAtt lyssna med stark volym långa stunder åt gången kan skada hörseln.""Vill du använda Aktivera tillgänglighet snabbt?""När kortkommandot har aktiverats startar du en tillgänglighetsfunktion genom att trycka ned båda volymknapparna i tre sekunder.""Vill du aktivera tillgänglighetsfunktionerna?"
- "Om du trycker ned båda volymknapparna i ett par sekunder aktiveras tillgänglighetsfunktionerna. Det kan leda till att enheten fungerar annorlunda.\n\nAktuella funktioner:\n%1$s\nDu kan ändra vilka funktioner som aktiveras under Inställningar > Tillgänglighet."
+ "Om du trycker ned båda volymknapparna i ett par sekunder aktiveras tillgänglighetsfunktionerna. Det kan få enheten ett fungera annorlunda.\n\nAktuella funktioner:\n%1$s\nDu kan ändra vilka funktioner som aktiveras under Inställningar > Tillgänglighet."" • %1$s\n""Vill du aktivera %1$s?""Om du trycker ned båda volymknapparna i ett par sekunder aktiveras %1$s, en tillgänglighetsfunktion. Det kan leda till att enheten fungerar annorlunda.\n\nDu kan ändra vilken funktion som ska aktiveras med genvägen under Inställningar > Tillgänglighet."
@@ -1645,7 +1642,7 @@
"Neka""Tryck på funktioner som du vill aktivera:""Välj vilka funktioner du vill använda med hjälp av tillgänglighetsknappen"
- "Välj vilka funktioner du vill använda med hjälp av kortkommandot för volymknapparna"
+ "Välj att funktioner att använda med hjälp av volymknappskortkommandot""%s har inaktiverats""Redigera genvägar""Klar"
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 2f3361794dda19d82a77247a0cb61e9505a9809f..7a5cdcffbefaeabbc0f4396beecfcef975465756 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -202,10 +202,8 @@
"Kipengele cha kuchapisha kimezimwa na %s.""Washa wasifu wako wa kazini""Programu zako za binafsi zimezuiwa hadi uwashe wasifu wako wa kazini"
-
-
-
-
+ "Programu za binafsi zitazuiwa tarehe %1$s saa %2$s. Msimamizi wako wa TEHAMA haruhusu wasifu wako wa kazini kuzimwa kwa zaidi ya siku %3$d."
+ "Washa""Mimi""Chaguo za kompyuta ndogo""Chaguo za Android TV"
@@ -1621,7 +1619,6 @@
"Umekosea kuchora mchoro wako wa kufungua mara %1$d. Baada ya majaribio %2$d yasiyofaulu, utaombwa kufungua simu yako kwa kutumia akaunti ya barua pepe.\n\n Jaribu tena baada ya sekunde %3$d."" — ""Ondoa"
- "Huduma ya programu inayotumika iliyoanzishwa chinichini kwenye %1$s haitakuwa na ruhusa inapotumika katika miundo ijayo ya R. Tafadhali angalia go/r-bg-fgs-restriction na uwasilishe ripoti ya hitilafu.""Ungependa kupandisha sauti zaidi ya kiwango kinachopendekezwa?\n\nKusikiliza kwa sauti ya juu kwa muda mrefu kunaweza kuharibu uwezo wako wa kusikia.""Ungependa kutumia njia ya mkato ya ufikivu?""Unapowasha kipengele cha njia ya mkato, hatua ya kubonyeza vitufe vyote viwili vya sauti kwa sekunde tatu itafungua kipengele cha ufikivu."
@@ -1654,7 +1651,7 @@
"Ugeuzaji rangi""Usahihishaji wa rangi""Vitufe vya sauti vilivyoshikiliwa. Umewasha %1$s."
- "Vitufe vya sauti vilivyoshikiliwa. Umezima %1$s."
+ "Vitufe vya sauti vimeshikiliwa. Umezima %1$s.""Bonyeza na ushikilie vitufe vyote viwili vya sauti kwa sekunde tatu ili utumie %1$s""Chagua kipengele utakachotumia ukigusa kitufe cha ufikivu:""Chagua kipengele cha kutumia pamoja na ishara ya ufikivu (telezesha vidole viwili kutoka chini kwenda juu kwenye skrini):"
@@ -2057,11 +2054,11 @@
"Kazini""Mwonekano wa binafsi""Mwonekano wa kazini"
- "Imeshindwa kushiriki maudhui haya na programu za kazini"
+ "Huwezi kushiriki maudhui haya na programu za kazini""Msimamizi wako wa TEHAMA hakuruhusu ushiriki maudhui haya ukitumia programu zilizo kwenye wasifu wako wa kazini""Huwezi kufungua maudhui haya ukitumia programu za kazini""Msimamizi wako wa TEHAMA hakuruhusu ufungue maudhui haya ukitumia programu zilizo kwenye wasifu wako wa kazini"
- "Imeshindwa kushiriki maudhui haya na programu za binafsi"
+ "Huwezi kushiriki maudhui haya na programu za binafsi""Msimamizi wako wa TEHAMA hakuruhusu ushiriki maudhui haya ukitumia programu zilizo kwenye wasifu wako wa binafsi""Huwezi kufungua maudhui haya ukitumia programu za binafsi""Msimamizi wako wa TEHAMA hakuruhusu ufungue maudhui haya ukitumia programu zilizo kwenye wasifu wako wa binafsi"
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index cc698c51fafd36e552cde7e9ffe3edf59f421f8b..32ea9795ed9d76c18329dfcbbb6cf730a6acec5c 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -202,10 +202,8 @@
"பிரிண்ட் செய்வதை %s தடுத்துள்ளது.""பணிக் கணக்கை ஆன் செய்யுங்கள்""பணிக் கணக்கை ஆன் செய்யும் வரை உங்கள் தனிப்பட்ட ஆப்ஸ் தடுக்கப்பட்டிருக்கும்"
-
-
-
-
+ "%1$s அன்று %2$s நேரத்தில் தனிப்பட்ட ஆப்ஸ் தடுக்கப்படும். பணிக் கணக்கை %3$d நாட்களுக்கு மேல் ஆஃப் செய்து வைத்திருப்பதை உங்கள் IT நிர்வாகி அனுமதிக்கவில்லை."
+ "ஆன் செய்""நான்""டேப்லெட் விருப்பங்கள்""Android TV விருப்பத்தேர்வுகள்"
@@ -1621,7 +1619,6 @@
"திறப்பதற்கான வடிவத்தை %1$d முறை தவறாக வரைந்துள்ளீர்கள். மேலும் %2$d தோல்வி முயற்சிகளுக்குப் பிறகு, மின்னஞ்சல் கணக்கைப் பயன்படுத்தி உங்கள் மொபைலைத் திறக்கக் கேட்கப்படுவீர்கள்.\n\n %3$d வினாடிகள் கழித்து முயற்சிக்கவும்."" — ""அகற்று"
- "%1$s இல் இருந்து பின்னணியில் தொடங்கப்பட்ட முன்புலச் சேவைக்கு, வரவுள்ள R பதிப்புகளில் உபயோகத்தின்போது மட்டுமான அனுமதி இருக்காது. go/r-bg-fgs-restriction என்பதைப் பார்த்து பிழை அறிக்கை ஒன்றைச் சமர்ப்பிக்கவும்.""பரிந்துரைத்த அளவை விட ஒலியை அதிகரிக்கவா?\n\nநீண்ட நேரத்திற்கு அதிகளவில் ஒலி கேட்பது கேட்கும் திறனைப் பாதிக்கலாம்.""அணுகல்தன்மை ஷார்ட்கட்டைப் பயன்படுத்தவா?""ஷார்ட்கட் இயக்கத்தில் இருக்கும்போது ஒலியளவு பட்டன்கள் இரண்டையும் 3 வினாடிகளுக்கு அழுத்தினால் அணுகல்தன்மை அம்சம் இயக்கப்படும்."
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index b6de1ac498664e11d5d0505824a9a52b22cf9535..2d3c68507e7a04ad645bae1bfe7dfe9f1a94d65b 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -202,10 +202,8 @@
"ముద్రణ %s ద్వారా నిలిపివేయబడింది.""మీ పని ప్రొఫైల్ను ఆన్ చేయి""మీరు మీ కార్యాలయ ప్రొఫైల్ను ప్రారంభించే వరకు, మీ వ్యక్తిగత యాప్లు బ్లాక్ చేయబడతాయి"
-
-
-
-
+ "%1$s తేదీన %2$sకు వ్యక్తిగత యాప్లు బ్లాక్ చేయబడతాయి. %3$d రోజులకు మించి మీ కార్యాలయ ప్రొఫైల్ను ఆఫ్లో ఉంచటానికి మీ IT అడ్మిన్ అనుమతించరు."
+ "ఆన్ చేయి""నేను""టాబ్లెట్ ఎంపికలు""Android TV ఎంపికలు"
@@ -1313,7 +1311,7 @@
"డీబగ్గింగ్ని నిలిపివేయడానికి ఎంచుకోండి.""వైర్లెస్ డీబగ్గింగ్ కనెక్ట్ చేయబడింది""వైర్లెస్ డీబగ్గింగ్ని ఆఫ్ చేయడానికి ట్యాప్ చేయండి"
- "వైర్లెస్ డీబగ్గింగ్ని డిజేబుల్ చేయడానికి ఎంచుకోండి."
+ "వైర్లెస్ డీబగ్గింగ్ను డిజేబుల్ చేయడానికి ఎంచుకోండి.""పరీక్ష నియంత్రణ మోడ్ ప్రారంభించబడింది""పరీక్ష నియంత్రణ మోడ్ను నిలిపివేయడానికి ఫ్యాక్టరీ రీసెట్ను అమలు చేయండి.""సీరియల్ కన్సోల్ ప్రారంభించబడింది"
@@ -1621,12 +1619,11 @@
"మీరు మీ అన్లాక్ నమూనాను %1$d సార్లు తప్పుగా గీసారు. మరో %2$d విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ ఫోన్ను అన్లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n %3$d సెకన్లలో మళ్లీ ప్రయత్నించండి."" — ""తీసివేయి"
- "%1$s నుండి, బ్యాక్గ్రౌండ్లో ప్రారంభమైన ఫోర్ గ్రౌండ్ సేవకు భవిష్యత్తు R బిల్డ్స్లో \'ఉపయోగంలో వున్నప్పుడు\' అనుమతి ఉండదు. దయచేసి go/r-bg-fgs-restrictionను చూసి బగ్ నివేదికను ఫైల్ చేయండి.""వాల్యూమ్ను సిఫార్సు చేయబడిన స్థాయి కంటే ఎక్కువగా పెంచాలా?\n\nసుదీర్ఘ వ్యవధుల పాటు అధిక వాల్యూమ్లో వినడం వలన మీ వినికిడి శక్తి దెబ్బ తినవచ్చు.""యాక్సెస్ సామర్థ్యం షార్ట్కట్ను ఉపయోగించాలా?""షార్ట్కట్ ఆన్ చేసి ఉన్నప్పుడు, రెండు వాల్యూమ్ బటన్లను 3 సెకన్ల పాటు నొక్కి ఉంచితే యాక్సెస్ సౌలభ్య ఫీచర్ ప్రారంభం అవుతుంది."
- "యాక్సెసిబిలిటీలను ఆన్ చేయాలా?"
- "రెండు వాల్యూమ్ కీలను కొంత సేపు నొక్కి పట్టుకోవడం ద్వారా యాక్సెసిబిలిటీలు ఆన్ అవుతాయి. ఇది మీ పరికరం పనిచేసే విధానాన్ని మార్చవచ్చు.\n\nప్రస్తుత ఫీచర్లు:\n%1$s\nఎంపిక చేసిన ఫీచర్లను మీరు సెట్టింగ్లు>యాక్సెసిబిలిటీలో మార్చవచ్చు."
+ "యాక్సెసిబిలిటీ ఫీచర్లను ఆన్ చేయాలా?"
+ "రెండు వాల్యూమ్ కీలను కొంత సేపు నొక్కి పట్టుకుంటే యాక్సెసిబిలిటీ ఫీచర్లు ఆన్ అవుతాయి. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nప్రస్తుత ఫీచర్లు:\n%1$s\nఎంపిక చేసిన ఫీచర్లను మీరు సెట్టింగ్లు>యాక్సెసిబిలిటీలో మార్చవచ్చు."" • %1$s\n""%1$s ఆన్ చేయాాలా?""రెండు వాల్యూమ్ కీలను కొన్ని సెకన్ల పాటు నొక్కి పట్టుకోవడం ద్వారా యాక్సెసిబిలిటీ అయిన %1$s ఆన్ అవుతుంది. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nసెట్టింగ్లు > యాక్సెసిబిలిటీలో, వేరొక ఫీచర్ను ప్రారంభించేలా ఈ షార్ట్ కట్ను మీరు మార్చవచ్చు."
@@ -2032,7 +2029,7 @@
%s + %d ఫైల్లు%s + %d ఫైల్
- "ఎవరికి షేర్ చేయాలనేదానికి సంబంధించి సిఫార్సులేవీ లేవు"
+ "ఎవరికి షేర్ చేయాలనే దానికి సంబంధించి సిఫార్సులేవీ లేవు""యాప్ల జాబితా""ఈ యాప్కు రికార్డ్ చేసే అనుమతి మంజూరు కాలేదు, అయినా ఈ USB పరికరం ద్వారా ఆడియోను క్యాప్చర్ చేయగలదు.""హోమ్"
@@ -2060,7 +2057,7 @@
"వర్క్ యాప్లతో దీనిని షేర్ చేయడం సాధ్యపడదు""మీ కార్యాలయ ప్రొఫైల్లోని యాప్లతో ఈ కంటెంట్ను మీరు షేర్ చేయడానికి మీ IT అడ్మిన్ అనుమతించరు""వర్క్ యాప్లతో తెరవడం సాధ్యపడదు"
- "మీ కార్యాలయ ప్రొఫైల్లోని యాప్లతో ఈ కంటెంట్ను మీరు తెరవడానికి మీ IT అడ్మిన్ అనుమతించరు"
+ "కార్యాలయ ప్రొఫైల్లోని యాప్లతో ఈ కంటెంట్ను మీరు తెరవడానికి మీ IT అడ్మిన్ అనుమతించరు""వ్యక్తిగత యాప్లతో దీనిని షేర్ చేయడం సాధ్యపడదు""మీ వ్యక్తిగత ప్రొఫైల్లోని యాప్లతో ఈ కంటెంట్ను మీరు షేర్ చేయడానికి మీ IT అడ్మిన్ అనుమతించరు""దీనిని, వ్యక్తిగత యాప్లతో తెరవడం సాధ్యపడదు"
diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml
index cb3d32815ea958352ed091cecd38054f855b6b34..d6bdeee00a9fde7e082a8f202cc9ec0c3f7fb974 100644
--- a/core/res/res/values-television/themes_device_defaults.xml
+++ b/core/res/res/values-television/themes_device_defaults.xml
@@ -33,5 +33,6 @@
-
+
+
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 84ca15ad2edffabe09e397056910653d56d6eeea..000ad10827a49cf9cb654b152a37ddd9f336f9c3 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -202,10 +202,8 @@
"%s ปิดใช้การพิมพ์แล้ว""เปิดโปรไฟล์งาน""แอปส่วนตัวจะถูกบล็อกไว้จนกว่าคุณจะเปิดโปรไฟล์งาน"
-
-
-
-
+ "แอปส่วนตัวจะถูกบล็อกในวันที่ %1$s เวลา %2$s ผู้ดูแลระบบไอทีไม่อนุญาตให้หยุดใช้โปรไฟล์งานเกิน %3$d วัน"
+ "เปิด""ฉัน""ตัวเลือกของแท็บเล็ต""ตัวเลือกของ Android TV"
@@ -1311,7 +1309,7 @@
"เชื่อมต่อการแก้ไขข้อบกพร่อง USB แล้ว""แตะเพื่อปิดการแก้ไขข้อบกพร่อง USB""เลือกเพื่อปิดใช้งานการแก้ไขข้อบกพร่อง USB"
- "เชื่อมต่อการแก้ไขข้อบกพร่องผ่าน Wi-Fi"
+ "เชื่อมต่อการแก้ไขข้อบกพร่องผ่าน Wi-Fi แล้ว""แตะเพื่อปิดการแก้ไขข้อบกพร่องผ่าน Wi-Fi""เลือกเพื่อปิดใช้การแก้ไขข้อบกพร่องผ่าน Wi-Fi""โหมดโปรแกรมทดสอบอัตโนมัติเปิดใช้อยู่"
@@ -1621,7 +1619,6 @@
"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง %1$d ครั้งแล้ว หากทำไม่สำเร็จอีก %2$d ครั้ง ระบบจะขอให้คุณปลดล็อกโทรศัพท์โดยใช้ับัญชีอีเมล\n\n โปรดลองอีกครั้งในอีก %3$d วินาที"" — ""ลบ"
- "บริการที่ทำงานอยู่เบื้องหน้าซึ่งเริ่มขึ้นในเบื้องหลังจาก %1$s จะไม่มีสิทธิ์ขณะใช้งานใน R บิลด์ต่อๆ ไป โปรดดู go/r-bg-fgs-restriction และส่งรายงานข้อบกพร่อง""นี่เป็นการเพิ่มระดับเสียงเกินระดับที่แนะนำ\n\nการฟังเสียงดังเป็นเวลานานอาจทำให้การได้ยินของคุณบกพร่องได้""ใช้ทางลัดการช่วยเหลือพิเศษไหม""เมื่อทางลัดเปิดอยู่ การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มนาน 3 วินาทีจะเริ่มฟีเจอร์การช่วยเหลือพิเศษ"
@@ -1652,7 +1649,7 @@
"ปิดทางลัด""ใช้ทางลัด""การกลับสี"
- "การปรับแก้สี"
+ "การแก้สี""กดปุ่มปรับระดับเสียงค้างไว้แล้ว เปิด %1$s แล้ว""กดปุ่มปรับระดับเสียงค้างไว้แล้ว ปิด %1$s แล้ว""กดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 3 วินาทีเพื่อใช้ %1$s"
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 395020d2ffc38d72419180d2d7328972b28e15b5..4cea142cbe60996d6a3d419a4322d78dc4f87ea0 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -202,10 +202,8 @@
"Na-disable ng %s ang pag-print.""I-on ang profile sa trabaho""Naka-block ang iyong mga personal na app hangga\'t hindi mo ino-on ang profile sa trabaho mo"
-
-
-
-
+ "Iba-block ang mga personal na app sa %1$s nang %2$s. Hindi pinapayagan ng iyong IT admin na manatiling naka-off ang profile mo sa trabaho nang mahigit %3$d (na) araw."
+ "I-on""Ako""Mga pagpipilian sa tablet""Mga opsyon sa Android TV"
@@ -1419,7 +1417,7 @@
"Ginagamit mo ang app na ito sa iyong profile sa trabaho""Pamamaraan ng pag-input""I-sync"
- "Pagiging Accessible"
+ "Accessibility""Wallpaper""Baguhin ang wallpaper""Notification listener"
@@ -1621,15 +1619,14 @@
"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang %1$d (na) beses. Pagkatapos ng %2$d pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang telepono mo gamit ang isang email account.\n\n Subukang muli sa loob ng %3$d (na) segundo."" — ""Alisin"
- "Ang sinimulan sa background na serbisyo sa foreground mula sa %1$s ay hindi magkakaroon ng pahintulot habang ginagamit sa mga R build sa hinaharap. Pakipuntahan ang go/r-bg-fgs-restriction at maghain ng bugreport.""Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."
- "Gagamitin ang Shortcut sa Pagiging Accessible?"
+ "Gagamitin ang Shortcut sa Accessibility?""Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume."
- "I-on ang mga feature ng pagiging accessible?"
- "Mao-on ang mga feature ng pagiging accessible kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nMga kasalukuyang feature:\n%1$s\nPuwede mong baguhin ang mga napiling feature sa Mga Setting > Pagiging Accessible."
+ "I-on ang mga feature ng accessibility?"
+ "Mao-on ang mga feature ng accessibility kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nMga kasalukuyang feature:\n%1$s\nPuwede mong baguhin ang mga napiling feature sa Mga Setting > Accessibility."" • %1$s\n""I-on ang %1$s?"
- "Mao-on ang feature ng pagiging accessible na %1$s kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nPuwede mong palitan ng ibang feature ang shortcut na ito sa Mga Setting > Pagiging Accessible."
+ "Mao-on ang feature ng accessibility na %1$s kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nPuwede mong palitan ng ibang feature ang shortcut na ito sa Mga Setting > Accessibility.""I-on""Huwag i-on""NAKA-ON"
@@ -1653,12 +1650,12 @@
"Gamitin ang Shortcut""Pag-invert ng Kulay""Pagwawasto ng Kulay"
- "Mga volume key na pinipindot nang matagal. Na-on ang %1$s."
- "Mga volume key na pinipindot nang matagal. Na-off ang %1$s."
+ "Pinindot nang matagal ang volume keys. Na-on ang %1$s."
+ "Pinindot nang matagal ang volume keys. Na-off ang %1$s.""Pindutin nang matagal ang parehong volume key sa loob ng tatlong segundo para magamit ang %1$s""Pumili ng feature na gagana sa pamamagitan ng pag-tap mo sa button ng accessibility:"
- "Pumili ng feature na gagana sa pamamagitan ng galaw ng pagiging accessible (pag-swipe pataas mula sa ibaba ng screen gamit ang dalawang daliri):"
- "Pumili ng feature na gagana sa pamamagitan ng galaw ng pagiging accessible (pag-swipe pataas mula sa ibaba ng screen gamit ang tatlong daliri):"
+ "Pumili ng feature na gagana sa pamamagitan ng galaw ng accessibility (pag-swipe pataas mula sa ibaba ng screen gamit ang dalawang daliri):"
+ "Pumili ng feature na gagana sa pamamagitan ng galaw ng accessibility (pag-swipe pataas mula sa ibaba ng screen gamit ang tatlong daliri):""Para magpalipat-lipat sa mga feature, pindutin nang matagal ang button ng accessibility.""Para magpalipat-lipat sa mga feature, mag-swipe pataas gamit ang dalawang daliri at i-hold ito.""Para magpalipat-lipat sa mga feature, mag-swipe pataas gamit ang tatlong daliri at i-hold ito."
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index c4da27c6de62d95b8c26bb47898f7bb37d2e0593..19bdba898eb9211578f327ab015895a79c676949 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -202,10 +202,8 @@
"Yazdırma işlemi %s tarafından devre dışı bırakıldı.""İş profilinizi açın""İş profilinizi açana kadar kişisel uygulamalarınız engellendi"
-
-
-
-
+ "Kişisel uygulamalar %1$s tarihinde saat %2$s itibarıyla engellenecektir. BT yöneticiniz, iş profilinizin %3$d günden fazla kapalı kalmasına izin vermiyor."
+ "Aç""Ben""Tablet seçenekleri""Android TV seçenekleri"
@@ -1621,7 +1619,6 @@
"Kilit açma deseninizi %1$d kez yanlış çizdiniz. %2$d başarısız denemeden sonra telefonunuzu bir e-posta hesabı kullanarak açmanız istenir.\n\n %3$d saniye içinde tekrar deneyin."" — ""Kaldır"
- "%1$s paketinden ön plan hizmetini başlatan arka plan, sonraki R derlemelerinde kullanım sırasında iznine sahip olmayacak. Lütfen go/r-bg-fgs-restriction sayfasına bakıp hata raporu girin.""Ses seviyesi önerilen düzeyin üzerine yükseltilsin mi?\n\nUzun süre yüksek ses seviyesinde dinlemek işitme duyunuza zarar verebilir.""Erişilebilirlik Kısayolu Kullanılsın mı?""Kısayol açıkken ses düğmelerinin ikisini birden 3 saniyeliğine basılı tutmanız bir erişilebilirlik özelliğini başlatır."
@@ -1653,8 +1650,8 @@
"Kısayolu Kullan""Rengi Ters Çevirme""Renk Düzeltme"
- "Ses tuşlarını basılı tutun. %1$s açıldı."
- "Ses tuşlarını basılı tutun. %1$s kapatıldı."
+ "Ses tuşlarını basılı tuttunuz. %1$s açıldı."
+ "Ses tuşlarını basılı tuttunuz. %1$s kapatıldı.""%1$s hizmetini kullanmak için her iki ses tuşunu basılı tutun""Erişilebilirlik düğmesine dokunduğunuzda kullanmak için bir özellik seçin:""Erişilebilirlik hareketiyle (iki parmakla ekranın altından yukarı kaydırma) kullanılacak bir özellik seçin:"
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 46f9fc954e117f04f60322257d229c58605ce717..04bb1cc33c61a34ffcf8863101a3301b403468d3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -206,10 +206,8 @@
"Додаток %s вимкнув друк.""Увімкніть робочий профіль""Особисті додатки заблоковано, доки ви не ввімкнете робочий профіль"
-
-
-
-
+ "Особисті додатки буде заблоковано %1$s о %2$s. Ваш ІТ-адміністратор не дозволяє, щоб робочий профіль був неактивний більше ніж %3$d дн."
+ "Увімкнути""Я""Парам. пристрою""Опції Android TV"
@@ -1351,9 +1349,9 @@
"Налагодження USB підключено""Торкніться, щоб вимкнути налагодження через USB""Виберіть, щоб вимкнути налагодження за USB"
- "Налагодження через Wi-Fi активне"
+ "Активне налагодження через Wi-Fi""Натисніть, щоб вимкнути налагодження через Wi-Fi"
- "Виберіть, щоб вимкнути налагодження через Wi-Fi."
+ "Натисніть, щоб вимкнути налагодження.""Увімкнено режим автоматизованого тестування""Щоб вимкнути режим автоматизованого тестування, відновіть заводські налаштування.""Послідовну консоль увімкнено"
@@ -1665,12 +1663,11 @@
"Ключ розблокування неправильно намальовано стільки разів: %1$d. У вас є ще стільки спроб: %2$d. У разі невдачі з’явиться запит розблокувати телефон за допомогою облікового запису електронної пошти.\n\n Повторіть спробу через %3$d сек."" – ""Вилучити"
- "Активний сервіс пакета %1$s, запущений у фоновому режимі, не матиме дозволу \"Коли додаток використовується\" в майбутніх складаннях R. Перегляньте go/r-bg-fgs-restriction і надішліть звіт про помилки.""Збільшити гучність понад рекомендований рівень?\n\nЯкщо слухати надто гучну музику тривалий час, можна пошкодити слух.""Використовувати швидке ввімкнення?""Якщо цей засіб увімкнено, ви можете активувати спеціальні можливості, утримуючи обидві кнопки гучності протягом трьох секунд.""Увімкнути спеціальні можливості?"
- "Якщо втримувати обидві клавіші гучності впродовж кількох секунд, буде ввімкнено спеціальні можливості. Це може вплинути на роботу пристрою.\n\nПоточні функції:\n%1$s\nВибрані функції можна змінити в меню \"Налаштування > Спеціальні можливості\"."
+ "Якщо втримувати обидві клавіші гучності впродовж кількох секунд, вмикаються спеціальні можливості. Це впливає на роботу пристрою.\n\nПоточні функції:\n%1$s\nВибрані функції можна змінити в налаштуваннях у меню спеціальних можливостей."" • %1$s\n""Увімкнути %1$s?""Якщо втримувати обидві клавіші гучності впродовж кількох секунд, буде ввімкнено спеціальні можливості – %1$s. Це може вплинути на роботу пристрою.\n\nДля цієї комбінації клавіш можна вибрати іншу функцію в меню \"Налаштування > Спеціальні можливості\"."
@@ -1822,7 +1819,7 @@
"Спробуйте пізніше""Перегляд на весь екран""Щоб вийти, проведіть пальцем зверху вниз."
- "Зрозуміло"
+ "OK""Готово""Вибір годин на циферблаті""Вибір хвилин на циферблаті"
@@ -2135,8 +2132,8 @@
"Ваш ІТ-адміністратор не дозволяє відкривати цей контент у додатках в особистому профілі""Робочий профіль призупинено""Увімкнути"
- "Робочі додатки не можуть підтримувати цей контент"
- "Робочі додатки не можуть відкривати цей контент"
+ "Немає робочих додатків для цього контенту"
+ "Немає робочих додатків, щоб відкрити цей контент""Особисті додатки не можуть підтримувати цей контент""Особисті додатки не можуть відкривати цей контент""PIN-код розблокування мережі SIM-карти"
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 3ec36bb9f7b018d65e66382f53903b6370fde880..0b44c5236f32f90a3f953b0fd791d86d9bb3903d 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -202,10 +202,8 @@
"%s نے پرنٹنگ کو غیر فعال کر دیا ہے۔""اپنی دفتری پروفائل آن کریں""آپ کے ذاتی ایپس کو اس وقت تک بلاک کر دیا جاتا ہے جب تک کہ آپ اپنے ورک پروفایل کو آن نہیں کرتے"
-
-
-
-
+ "%1$s کو %2$s بجے ذاتی ایپس کو مسدود کر دیا جائے گا۔ آپ کا IT منتظم آپ کی دفتری پروفائل کو %3$d دن سے زیادہ بند رکھنے کی اجازت نہیں دیتا ہے۔"
+ "آن کریں""میں""ٹیبلیٹ کے اختیارات""Android TV اختیارات"
@@ -1130,8 +1128,8 @@
"اس کے ساتھ کھولیں""%1$s کے ساتھ کھولیں""کھولیں"
- "%1$s لنکس کے ساتھ کھولیں"
- "لنکس کے ساتھ کھولیں"
+ "%1$s لنکس اس کے ساتھ کھولیں"
+ "اس کے ساتھ لنکس کھولیں""%1$s کے ذریعے لنکس کھولیں""%1$s لنکس کو %2$s کے ذریعے کھولیں""رسائی دیں"
@@ -1313,7 +1311,7 @@
"USB ڈیبگ کرنے کو غیر فعال کرنے کیلئے منتخب کریں۔""وائرلیس ڈیبگنگ منسلک ہے""وائرلیس ڈیبگنگ آف کرنے کے لیے تھپتھپائیں"
- "وائرلیس ڈیبگنگ کرنے کو غیر فعال کرنے کے ليے منتخب کریں۔"
+ "وائرلیس ڈیبگنگ کو غیر فعال کرنے کے ليے منتخب کریں۔""ٹیسٹ ہارنیس موڈ فعال ہے""ٹیسٹ ہارنیس موڈ غیر فعال کرنے کے لیے فیکٹری ری سیٹ کریں۔""شمار کونسول فعال ہے"
@@ -1588,7 +1586,7 @@
"اپنا پیٹرن ڈرا کریں""SIM PIN درج کریں"
- "PIN درج کریں"
+ "PIN درج کریں""پاس ورڈ درج کریں""SIM اب غیر فعال ہوگیا ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔""پسندیدہ PIN کوڈ درج کریں"
@@ -1621,7 +1619,6 @@
"آپ نے اپنا غیر مقفل کرنے کا پیٹرن %1$d بار غلط طریقے سے ڈرا کیا ہے۔ %2$d مزید ناکام کوششوں کے بعد، آپ سے ایک ای میل اکاؤنٹ استعمال کرکے اپنا فون غیر مقفل کرنے کو کہا جائے گا۔\n\n %3$d سیکنڈ میں دوبارہ کوشش کریں۔"" — ""ہٹائیں"
- "پس منظر %1$s سے شروع کی گئی پیش منظر کی سروس کو مستقبل کے R بلڈز میں استعمال کے دوران اجازت نہیں ہوگی۔ براہ کرم go/r-bg-fgs-restriction دیکھیں اور بگ رپورٹ دائر کریں۔""والیوم کو تجویز کردہ سطح سے زیادہ کریں؟\n\nزیادہ وقت تک اونچی آواز میں سننے سے آپ کی سماعت کو نقصان پہنچ سکتا ہے۔""ایکسیسبیلٹی شارٹ کٹ استعمال کریں؟""شارٹ کٹ آن ہونے پر، 3 سیکنڈ تک دونوں والیوم بٹنز کو دبانے سے ایک ایکسیسبیلٹی خصوصیت شروع ہو جائے گی۔"
@@ -1761,7 +1758,7 @@
"%s سروس انسٹال ہو گئی""فعال کرنے کیلئے تھپتھپائیں""منتظم PIN درج کریں"
- "PIN درج کریں"
+ "PIN درج کریں""غلط""موجودہ PIN""نیا PIN"
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 27b3e726140e5d2eea039140b5101569d498a6e0..e07a81d5892ab35407c9421f1d3a3e3dec035b8d 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -202,10 +202,8 @@
"Chop etish funksiyasi %s tomonidan faolsizlantirilgan.""Ish profilingizni yoqing""Ish profilingiz yoniqligida shaxsiy ilovalaringiz bloklanadi"
-
-
-
-
+ "Shaxsiy ilovalar %1$s%2$s da bloklanadi. AT administratoringiz ish profili %3$d kundan ortiq oʻchiq qolishini taqiqlagan"
+ "Yoqish""Men""Planshet sozlamalari""Android TV parametrlari"
@@ -1092,7 +1090,7 @@
"%1$02d:%2$02d""%1$d:%2$02d:%3$02d""Hammasini belgilash"
- "Kesish"
+ "Kesib olish""Nusxa olish""Vaqtinchalik xotiraga nusxalab bo‘lmadi""Joylash"
@@ -1308,12 +1306,12 @@
"Ulangan qurilma quvvatlanmoqda. Boshqa parametrlar uchun bosing.""Analogli audio uskuna aniqlandi""Biriktirilgan qurilma mazkur telefon bilan mos emas. Batafsil axborot olish uchun bu yerga bosing."
- "USB orqali nosozliklarni aniqlash"
- "USB orqali nosozliklarni aniqlashni faolsizlantirish uchun bosing"
+ "USB debagging ulandi"
+ "USB debaggingni uzish uchun bosing""USB orqali nosozliklarni tuzatishni o‘chirib qo‘yish uchun bosing.""Wi-Fi orqali debagging yoqildi""Wi-Fi orqali debagging uzilishi uchun bosing"
- "Wi-Fi orqali debaggingni faolsizlantirish uchun bosing."
+ "Uni faolsizlantirish uchun bosing.""Xavfsizlik sinovi rejimi yoqildi""Xavfsizlik sinovi rejimini faolsizlantirish uchun zavod sozlamalariga qaytaring.""Ketma-ket port konsoli yoqildi"
@@ -1621,7 +1619,6 @@
"Siz grafik kalitni %1$d marta noto‘g‘ri chizdingiz. %2$d marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, telefon qulfini ochishingiz so‘raladi.\n\n %3$d soniyadan so‘ng yana urinib ko‘ring."" — ""Olib tashlash"
- "Fonda faol %1$s xizmatini ishga tushirish uchun kelgusi R nashrlarida ishlatilayotganda ruxsat berish imkoniyati boʻlmaydi. go/r-bg-fgs-restriction sahifasiga kiring va xatolik hisobotini yuboring.""Tovush balandligi tavsiya etilgan darajadan ham yuqori qilinsinmi?\n\nUzoq vaqt davomida baland ovozda tinglash eshitish qobiliyatingizga salbiy ta’sir ko‘rsatishi mumkin.""Tezkor ishga tushirishdan foydalanilsinmi?""Maxsus imkoniyatlar funksiyasidan foydalanish uchun u yoniqligida ikkala tovush tugmasini 3 soniya bosib turing."
@@ -1643,9 +1640,9 @@
"Ilova yoki qurilma sensori bilan munosabatlaringizni kuzatishi hamda sizning nomingizdan ilovalar bilan ishlashi mumkin.""Ruxsat""Rad etish"
- "Funksiyadan foydalanish uchun ustiga bosing:"
+ "Kerakli funksiyani tanlang""Maxsus imkoniyatlar tugmasi bilan foydalanish uchun funksiyalarni tanlang"
- "Tovush balandligi tugmasi bilan foydalanish uchun funksiyalarni tanlang"
+ "Tovush tugmasi bilan ishga tushiriladigan funksiyalarni tanlang""%s faolsizlantirildi""Tezkor tugmalarni tahrirlash""OK"
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 5fe12fd1b43c308c23dcfeeb9a72fda57edb65ad..74b7ac89f31538ce0649bf95b43077a0c55c339d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -202,10 +202,8 @@
"%s đã tắt tính năng in.""Bật hồ sơ công việc của bạn""Ứng dụng cá nhân của bạn bị chặn cho tới khi bạn bật hồ sơ công việc"
-
-
-
-
+ "Ứng dụng cá nhân sẽ bị chặn lúc %2$s vào %1$s. Quản trị viên CNTT không cho phép bạn tắt hồ sơ công việc quá %3$d ngày."
+ "Bật""Tôi""Tùy chọn máy tính bảng""Tùy chọn dành cho Android TV"
@@ -538,7 +536,7 @@
"Cho phép ứng dụng này sửa đổi bộ sưu tập ảnh của bạn.""đọc vị trí từ bộ sưu tập phương tiện""Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."
- "Xác minh đó là bạn"
+ "Xác minh danh tính của bạn""Không có phần cứng sinh trắc học""Đã hủy xác thực""Không nhận dạng được"
@@ -1015,7 +1013,7 @@
"tuần""năm""năm"
- "bây giờ"
+ "ngay lúc này"%dph%dph
@@ -1621,7 +1619,6 @@
"Bạn đã %1$d lần vẽ không chính xác hình mở khóa của mình. Sau %2$d lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa điện thoại bằng tài khoản email.\n\n Vui lòng thử lại sau %3$d giây."" — ""Xóa"
- "Dịch vụ trên nền trước đã bắt đầu ở nền từ %1$s sẽ không có quyền khi đang sử dụng trong các bản dựng R trong tương lai. Vui lòng xem go/r-bg-fgs-restriction và gửi báo cáo lỗi.""Bạn tăng âm lượng lên quá mức khuyên dùng?\n\nViệc nghe ở mức âm lượng cao trong thời gian dài có thể gây tổn thương thính giác của bạn.""Sử dụng phím tắt Hỗ trợ tiếp cận?""Khi phím tắt này đang bật, thao tác nhấn cả hai nút âm lượng trong 3 giây sẽ mở tính năng hỗ trợ tiếp cận."
@@ -2032,7 +2029,7 @@
%s + %d tệp%s + %d tệp
- "Không có người nào được đề xuất để chia sẻ"
+ "Không có gợi ý về người để chia sẻ""Danh sách ứng dụng""Ứ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.""Màn hình chính"
@@ -2054,7 +2051,7 @@
"Cuộc trò chuyện nhóm""%1$d+""Cá nhân"
- "Cơ quan"
+ "Công việc""Chế độ xem cá nhân""Chế độ xem công việc""Không thể chia sẻ nội dung này với các ứng dụng công việc"
@@ -2065,7 +2062,7 @@
"Quản trị viên CNTT không cho phép chia sẻ nội dung này với các ứng dụng trong hồ sơ cá nhân của bạn""Không thể mở nội dung này bằng các ứng dụng cá nhân""Quản trị viên CNTT không cho phép mở nội dung này bằng các ứng dụng trong hồ sơ cá nhân của bạn"
- "Hồ sơ công việc bị tạm dừng"
+ "Hồ sơ công việc đã bị tạm dừng""Bật""Các ứng dụng công việc không hỗ trợ nội dung này""Các ứng dụng công việc không thể mở nội dung này"
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 9faa93ad9a5e099123d22a39da392a554a0240b0..d04c6187d6c6a7236629c26acccc945fdf164135 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -92,7 +92,7 @@
"紧急回拨模式""移动数据状态""短信"
- "语音邮件"
+ "语音信息""WLAN 通话""SIM 卡状态""高优先顺序 SIM 卡状态"
@@ -202,10 +202,8 @@
"“%s”已停用打印功能。""开启工作资料""系统已屏蔽您的个人应用。您需要开启工作资料,系统才会取消屏蔽这些应用"
-
-
-
-
+ "系统将于 %1$s%2$s 屏蔽个人应用。IT 管理员不允许您的工作资料保持关闭状态超过 %3$d 天。"
+ "开启""我""平板电脑选项""Android TV 选项"
@@ -960,7 +958,7 @@
"允许该应用修改您手机上存储的浏览器历史记录或浏览器书签。此权限可让该应用清除或修改浏览器数据。请注意:此权限可能不适用于第三方浏览器或具备网页浏览功能的其他应用。""设置闹钟""允许应用在已安装的闹钟应用中设置闹钟。有些闹钟应用可能无法实现此功能。"
- "添加语音邮件"
+ "添加语音信息""允许应用在您的语音信箱中留言。""修改“浏览器”地理位置的权限""允许应用修改“浏览器”的地理位置权限。恶意应用可能借此向任意网站发送位置信息。"
@@ -1621,7 +1619,6 @@
"您已连续 %1$d 次画错解锁图案。如果再尝试 %2$d 次后仍不成功,系统就会要求您使用自己的电子邮件帐号解锁手机。\n\n请在 %3$d 秒后重试。"" — ""删除"
- "在未来的 R 版本中,在后台启动的 %1$s 中的前台服务将不具有仅在使用时授予的权限。请访问 go/r-bg-fgs-restriction 并提交错误报告。""要将音量调高到建议的音量以上吗?\n\n长时间保持高音量可能会损伤听力。""要使用无障碍快捷方式吗?""启用这项快捷方式后,同时按下两个音量按钮 3 秒钟即可启动无障碍功能。"
@@ -1653,8 +1650,8 @@
"使用快捷方式""颜色反转""色彩校正"
- "按住音量键。%1$s已开启。"
- "按住音量键。%1$s已关闭。"
+ "已按住音量键。%1$s已开启。"
+ "已按住音量键。%1$s已关闭。""同时按住两个音量键 3 秒钟即可使用 %1$s""选择点按“无障碍”按钮后要使用的功能:""选择要搭配无障碍手势(用两根手指从屏幕底部向上滑动)使用的功能:"
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 66bc070c4775dbf1d48ce54d62790e3766e59671..458c3619c02feda7c00eadc89708f9c17d5bf9c5 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -202,10 +202,8 @@
"「%s」暫停了列印。""開啟工作設定檔""系統會封鎖您的個人應用程式,直至您開啟工作設定檔為止"
-
-
-
-
+ "個人應用程式將於 %1$s%2$s 封鎖。IT 管理員不允許您的工作設定檔保持關閉狀態超過 %3$d 天。"
+ "開啟""我本人""平板電腦選項""Android TV 選項"
@@ -1621,11 +1619,10 @@
"您已畫錯解鎖圖案 %1$d 次,如果再嘗試 %2$d 次仍未成功,系統會要求您透過電郵帳戶解開上鎖的手機。\n\n請在 %3$d 秒後再試一次。"" — ""移除"
- "從「%1$s」啟動前景服務的背景將不會在未來的 R 版本中提供「僅在使用此應用程式時允許」權限。請參閱 go/r-bg-fgs-restriction,提交錯誤報告。""要調高音量 (比建議的音量更大聲) 嗎?\n\n長時間聆聽高分貝音量可能會導致您的聽力受損。""要使用無障礙功能快速鍵嗎?""啟用快速鍵後,同時按住音量按鈕 3 秒便可啟用無障礙功能。"
- "要啟用無障礙功能嗎?"
+ "要開啟無障礙功能嗎?""同時按下兩個音量鍵幾秒,以開啟無障礙功能。這可能會變更裝置的運作。\n\n目前功能:\n%1$s\n您可在「設定」>「無障礙功能」中變更所選功能。"" • %1$s\n""要啟用 %1$s 嗎?"
@@ -1645,7 +1642,7 @@
"拒絕""輕按即可開始使用所需功能:""選擇要配搭無障礙功能按鈕使用的功能"
- "選擇要配搭音量快速鍵使用的功能"
+ "選擇要用音量快速鍵的功能""%s 已關閉""編輯捷徑""完成"
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6a7391bb0f56847574acf312a8bb557e005f8503..72dd4581e8fe0090079c938e72ee6edec9bdb869 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -202,10 +202,8 @@
"「%s」已停用列印功能。""開啟工作資料夾""系統已封鎖你的個人應用程式;你必須開啟工作資料夾,這些應用程式才會解除封鎖"
-
-
-
-
+ "系統將於 %1$s%2$s 封鎖個人應用程式。IT 管理員不允許工作資料夾關閉超過 %3$d 天。"
+ "開啟""我""平板電腦選項""Android TV 選項"
@@ -1621,12 +1619,11 @@
"你的解鎖圖案已畫錯 %1$d 次,如果再嘗試 %2$d 次仍未成功,系統就會要求你透過電子郵件帳戶解除手機的鎖定狀態。\n\n請在 %3$d 秒後再試一次。"" — ""移除"
- "來自「%1$s」的背景啟動前景服務不會具備未來 R 版本的使用狀態權限。請前往 go/r-bg-fgs-restriction 並提交錯誤報告。""要調高音量,比建議的音量更大聲嗎?\n\n長時間聆聽高分貝音量可能會使你的聽力受損。""要使用無障礙捷徑嗎?""啟用捷徑功能,只要同時按下兩個音量按鈕 3 秒,就能啟動無障礙功能。""要開啟無障礙功能嗎?"
- "同時按住音量調高鍵和調低鍵數秒,即可開啟無障礙功能。這麼做可能會改變裝置的運作方式。\n\n目前的功能:\n%1$s\n你可以在 [設定] > [無障礙設定] 中變更所選功能。"
+ "同時按住音量調高鍵和調低鍵數秒,即可開啟無障礙功能。這麼做可能會改變裝置的運作方式。\n\n目前的功能:\n%1$s\n你可以在 [設定] > [無障礙設定] 中變更選取的功能。"" • %1$s\n""要開啟「%1$s」嗎?""同時按住音量調高鍵和調低鍵數秒,即可開啟「%1$s」無障礙功能。這麼做可能會改變裝置的運作方式。\n\n你可以在 [設定] > [無障礙設定] 中變更這個快速鍵觸發的功能。"
@@ -1795,8 +1792,8 @@
"已由你的管理員更新""已由你的管理員刪除""確定"
- "為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n""瞭解詳情"
- "為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞"
+ "為了延長電池續航力,省電模式會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n""瞭解詳情"
+ "為了延長電池續航力,省電模式會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞""「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你目前使用的應用程式可以存取資料,但存取頻率可能不如平時高。舉例來說,圖片可能不會自動顯示,在你輕觸後才會顯示。""要開啟數據節省模式嗎?""開啟"
@@ -1921,7 +1918,7 @@
"社交和通訊""新聞和雜誌""地圖和導航"
- "生產應用"
+ "工作效率""裝置儲存空間""USB 偵錯""點"
@@ -2002,9 +1999,9 @@
"顯示在畫面上的其他應用程式上層""日常安排模式資訊通知""電池電力可能會在你平常的充電時間前耗盡"
- "已啟用節約耗電量模式以延長電池續航力"
- "節約耗電量"
- "節約耗電量模式已關閉"
+ "已啟用省電模式以延長電池續航力"
+ "省電模式"
+ "省電模式已關閉""手機電力充足,各項功能不再受到限制。""平板電腦電力充足,各項功能不再受到限制。""裝置電力充足,各項功能不再受到限制。"
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 946e4eac7532993940609e77dfee86888bba91cc..4fe07806dc301a7d981240b6f85c37f48404d494 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -202,10 +202,8 @@
"Ukuphrinta kukhutshazwe nge-%s.""Vula iphrofayela yakho yomsebenzi""Izinhlelo zakho zokusebenza zomuntu siqu zivinjelwe kuze kube yilapho uvula iphrofayela yakho yomsebenzi"
-
-
-
-
+ "Izinhlelo zokusebenza zomuntu siqu zizovinjelwa ngomhla ka-%1$s ngo-%2$s. Umphathi wakho we-IT akavumeli ukuthi iphrofayela yakho yomsebenzi ihlale ivaliwe izinsuku ezingaphezu kwezi-%3$d."
+ "Vula""Mina""Okukhethwa kukho kwethebhulethi""Izinketho ze-Android TV"
@@ -1311,7 +1309,7 @@
"Ukulungisa iphutha le-USB kuxhunyiwe""Thepha ukuze uvale ukulungisa amaphutha kwe-USB""Khetha ukuvimbela ukulungisa iphutha le-USB."
- "Ukulungisa amaphutha okungenantambo kuxhunyiwe"
+ "Ukulungisa amaphutha e-wireless kuxhunyiwe""Thepha ukuze ucishe ukulungisa amaphutha okungenantambo""Khetha ukukhubaza ukulungisa amaphutha okungenantambo.""Imodi yokuhlola i-harness inikwe amandla"
@@ -1621,7 +1619,6 @@
"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezi-%1$d Emva kweminye imizamo engu-%2$d, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google\n\n Zame futhi emumva kwengu- %3$d amasekhondi."" — ""Susa"
- "Ingemuva eqalise isevisi yasemuva kusuka ku-%1$s ngeke ithole imvume yokusebenzisa yesikhathi ekwakheni kwe-R ezayo. Sicela ubone i-go/r-bg-fgs-restriction bese ufayele umbiko wesiphazamiso.""Khuphukisa ivolumu ngaphezu kweleveli enconyiwe?\n\nUkulalela ngevolumu ephezulu izikhathi ezide kungahle kulimaze ukuzwa kwakho.""Sebenzisa isinqamuleli sokufinyelela?""Uma isinqamuleli sivuliwe, ukucindezela zombili izinkinobho zevolumu amasekhondi angu-3 kuzoqalisa isici sokufinyelela."
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c962256e477cd7aa9217b7c68b89a9d87dcf1656..f42b248f46707199d63f4a117add2c198117c625 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1830,30 +1830,13 @@
-
+
-
+
-
+
-
+
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index c413f8b036d773bb6761e8dfcd495d0ec310b163..1242c6dc82173bcc0c6e12a9aacefc76d1f0e0b7 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -230,6 +230,7 @@
#ffC4C6C6#FF202124#FF5F6368
+ #FF1A73E8#1A73E8
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2cad9e6888a61e667078cd9bcf8f7117d4895735..39d20bbff3ba992ed140723cadf15b031285d81d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -403,33 +403,18 @@
2
-
+
-
+
-
-
-
-
-
-
-
-
-
+
@@ -437,7 +422,8 @@
updated config_tether_dhcp_range has to be updated appropriately. -->
5
-
+
@@ -477,31 +463,8 @@
-->
-
-
+
-
+
-
+
-
+
+
24
-
+
com.android.settings/.wifi.tether.TetherService
-
+
170
-
- true
-
@@ -2860,9 +2770,9 @@
-->
emergency
+ lockdownpowerrestart
- lockdownlogoutbugreport
@@ -4313,10 +4223,6 @@
notifications until they target R -->
-
-
-
3
@@ -4456,11 +4362,4 @@
false-1
-
-
-
-
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index ad3d20ee5f24ebc4e81f1f44041ce2e1972cec40..ebaf85c64a1421e7cb902dff2e52ef8bd71e284a 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -273,6 +273,9 @@
2dp
+
+ 4dp
+
-2px
@@ -312,6 +315,12 @@
8dp
+
+ 3dp
+
+
+ 12dp
+
6dp
@@ -713,13 +722,19 @@
1dp
- 14dp
+ 17dp
+
+ 2dp
+
+ 10dp
+
+ 20dp
- 9dp
+ 12dp
- 17.5dp
+ 9dp38dp
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2869021a8a72d5cd8e5e6a47180b4f054cb17cfb..e2fbbf46608fbe47c747a90f0d916607caad4397 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3022,6 +3022,8 @@
+
+
-
+
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dc21e878d13276cfe2b01cd6a9e2d727fab527af..0c8745392f5e09e92f5919aa4212e5b9aba17886 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1613,15 +1613,15 @@
Can\u2019t store new face data. Delete an old one first.Face operation canceled.
-
+
Face unlock canceled by user.Too many attempts. Try again later.
-
+
Too many attempts. Face unlock disabled.Can\u2019t verify face. Try again.
-
+
You haven\u2019t set up face unlock.Face unlock is not supported on this device.
@@ -4363,9 +4363,6 @@
Remove
-
- The background started foreground service from %1$s will not have while-in-use permission in future R builds. Please see go/r-bg-fgs-restriction and file a bugreport.
-
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index e9ac679ec39c9864213a2d57cd7333afbe873bc9..ef019ba927693f1519ce08aa0db7b46d0dbf36c2 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -296,9 +296,12 @@ easier.
+
+
+
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f30d482e06b2a382aed9098ae53b8a7f1d2b9de6..871924255e66313af01b11691d0b62dab6682c3c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1785,6 +1785,7 @@
+
@@ -1850,10 +1851,8 @@
-
-
@@ -2766,6 +2765,7 @@
+
@@ -3533,6 +3533,8 @@
+
+
@@ -3823,10 +3825,10 @@
-
+
@@ -3834,7 +3836,9 @@
+
+
@@ -3897,6 +3901,12 @@
+
+
+
+
+
+
@@ -3944,6 +3954,13 @@
+
+
+
+
+
+
+
@@ -3989,9 +4006,6 @@
-
-
-
@@ -4033,8 +4047,5 @@
-
-
-
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index a80725c2758b293cc5dff985d4632c25118a1156..9dca9128e36280fd68617a6f9c64347ce0dc9818 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -128,6 +128,10 @@
@style/Widget.DeviceDefault.Toolbar
+
+
+ @dimen/resolver_icon_size
+ @dimen/resolver_badge_size
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 70917e76f8b40fa0591ad508c47dee715a9c4425..d5733e34a8ef6508cf10ae17e99c1f966d294436 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -39,7 +39,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp
new file mode 100644
index 0000000000000000000000000000000000000000..e74f30ee10a4644e6858b368bf13cafd5289d478
--- /dev/null
+++ b/core/tests/PackageInstallerSessions/Android.bp
@@ -0,0 +1,42 @@
+//
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+ name: "FrameworksCorePackageInstallerSessionsTests",
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "frameworks-base-testutils",
+ "platform-test-annotations",
+ "testng",
+ "truth-prebuilt",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "framework",
+ "framework-res",
+ ],
+
+ platform_apis: true,
+ sdk_version: "core_platform",
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/PackageInstallerSessions/AndroidManifest.xml b/core/tests/PackageInstallerSessions/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5b22d2b4f3e3715b77bf525c2013482648445e05
--- /dev/null
+++ b/core/tests/PackageInstallerSessions/AndroidManifest.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
new file mode 100644
index 0000000000000000000000000000000000000000..494c92a8aa3f13db5ce29934122b336b4b021bd4
--- /dev/null
+++ b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
@@ -0,0 +1,188 @@
+/*
+ * 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.content.pm
+
+import android.content.Context
+import android.content.pm.PackageInstaller.SessionParams
+import android.platform.test.annotations.Presubmit
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.LargeTest
+import com.android.compatibility.common.util.ShellIdentityUtils
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.testng.Assert.assertThrows
+import kotlin.random.Random
+
+/**
+ * For verifying public [PackageInstaller] session APIs. This differs from
+ * [com.android.server.pm.PackageInstallerSessionTest] in services because that mocks the session,
+ * whereas this test uses the installer on device.
+ */
+@Presubmit
+class PackageSessionTests {
+
+ companion object {
+ /**
+ * Permissions marked "hardRestricted" or "softRestricted" in core/res/AndroidManifest.xml.
+ */
+ private val RESTRICTED_PERMISSIONS = listOf(
+ "android.permission.SEND_SMS",
+ "android.permission.RECEIVE_SMS",
+ "android.permission.READ_SMS",
+ "android.permission.RECEIVE_WAP_PUSH",
+ "android.permission.RECEIVE_MMS",
+ "android.permission.READ_CELL_BROADCASTS",
+ "android.permission.ACCESS_BACKGROUND_LOCATION",
+ "android.permission.READ_CALL_LOG",
+ "android.permission.WRITE_CALL_LOG",
+ "android.permission.PROCESS_OUTGOING_CALLS"
+ )
+ }
+
+ private val context: Context = InstrumentationRegistry.getContext()
+
+ private val installer = context.packageManager.packageInstaller
+
+ @Before
+ @After
+ fun abandonAllSessions() {
+ installer.mySessions.asSequence()
+ .map { it.sessionId }
+ .forEach {
+ try {
+ installer.abandonSession(it)
+ } catch (ignored: Exception) {
+ // Querying for sessions checks by calling package name, but abandoning
+ // checks by UID, which won't match if this test failed to clean up
+ // on a previous install + run + uninstall, so ignore these failures.
+ }
+ }
+ }
+
+ @Test
+ fun truncateAppLabel() {
+ val longLabel = invalidAppLabel()
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
+ setAppLabel(longLabel)
+ }
+
+ createSession(params) {
+ assertThat(installer.getSessionInfo(it)?.appLabel)
+ .isEqualTo(longLabel.take(PackageItemInfo.MAX_SAFE_LABEL_LENGTH))
+ }
+ }
+
+ @Test
+ fun removeInvalidAppPackageName() {
+ val longName = invalidPackageName()
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
+ setAppPackageName(longName)
+ }
+
+ createSession(params) {
+ assertThat(installer.getSessionInfo(it)?.appPackageName)
+ .isEqualTo(null)
+ }
+ }
+
+ @Test
+ fun removeInvalidInstallerPackageName() {
+ val longName = invalidPackageName()
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
+ setInstallerPackageName(longName)
+ }
+
+ createSession(params) {
+ // If a custom installer name is dropped, it defaults to the caller
+ assertThat(installer.getSessionInfo(it)?.installerPackageName)
+ .isEqualTo(context.packageName)
+ }
+ }
+
+ @Test
+ fun truncateWhitelistPermissions() {
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
+ setWhitelistedRestrictedPermissions(invalidPermissions())
+ }
+
+ createSession(params) {
+ assertThat(installer.getSessionInfo(it)?.whitelistedRestrictedPermissions!!)
+ .containsExactlyElementsIn(RESTRICTED_PERMISSIONS)
+ }
+ }
+
+ @LargeTest
+ @Test
+ fun allocateMaxSessionsWithPermission() {
+ ShellIdentityUtils.invokeWithShellPermissions {
+ repeat(1024) { createDummySession() }
+ assertThrows(IllegalStateException::class.java) { createDummySession() }
+ }
+ }
+
+ @LargeTest
+ @Test
+ fun allocateMaxSessionsNoPermission() {
+ repeat(50) { createDummySession() }
+ assertThrows(IllegalStateException::class.java) { createDummySession() }
+ }
+
+ private fun createDummySession() {
+ installer.createSession(SessionParams(SessionParams.MODE_FULL_INSTALL)
+ .apply {
+ setAppPackageName(invalidPackageName())
+ setAppLabel(invalidAppLabel())
+ setWhitelistedRestrictedPermissions(invalidPermissions())
+ })
+ }
+
+ private fun invalidPackageName(maxLength: Int = SessionParams.MAX_PACKAGE_NAME_LENGTH): String {
+ return (0 until (maxLength + 10))
+ .asSequence()
+ .mapIndexed { index, _ ->
+ // A package name needs at least one separator
+ if (index == 2) {
+ '.'
+ } else {
+ Random.nextInt('z' - 'a').toChar() + 'a'.toInt()
+ }
+ }
+ .joinToString(separator = "")
+ }
+
+ private fun invalidAppLabel() = (0 until PackageItemInfo.MAX_SAFE_LABEL_LENGTH + 10)
+ .asSequence()
+ .map { Random.nextInt(Char.MAX_VALUE.toInt()).toChar() }
+ .joinToString(separator = "")
+
+ private fun invalidPermissions() = RESTRICTED_PERMISSIONS.toMutableSet()
+ .apply {
+ // Add some invalid permission names
+ repeat(10) { add(invalidPackageName(300)) }
+ }
+
+ private fun createSession(params: SessionParams, block: (Int) -> Unit = {}) {
+ val sessionId = installer.createSession(params)
+ try {
+ block(sessionId)
+ } finally {
+ installer.abandonSession(sessionId)
+ }
+ }
+}
diff --git a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java b/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
index c72707db9560d445964a3e22294c1763c10703fb..153337727e967f4ff321dbb98f5f99c64bd51977 100644
--- a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
@@ -58,6 +58,8 @@ public class BugreportManagerTest {
private Handler mHandler;
private Executor mExecutor;
private BugreportManager mBrm;
+ private File mBugreportFile;
+ private File mScreenshotFile;
private ParcelFileDescriptor mBugreportFd;
private ParcelFileDescriptor mScreenshotFd;
@@ -73,8 +75,10 @@ public class BugreportManagerTest {
};
mBrm = getBugreportManager();
- mBugreportFd = parcelFd("bugreport_" + name.getMethodName(), ".zip");
- mScreenshotFd = parcelFd("screenshot_" + name.getMethodName(), ".png");
+ mBugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip");
+ mScreenshotFile = createTempFile("screenshot_" + name.getMethodName(), ".png");
+ mBugreportFd = parcelFd(mBugreportFile);
+ mScreenshotFd = parcelFd(mScreenshotFile);
getPermissions();
}
@@ -120,6 +124,21 @@ public class BugreportManagerTest {
assertFdsAreClosed(mBugreportFd);
}
+ @Test
+ public void normalFlow_full() throws Exception {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+ mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback);
+
+ waitTillDoneOrTimeout(callback);
+ assertThat(callback.isDone()).isTrue();
+ assertThat(callback.getErrorCode()).isEqualTo(
+ BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+ // bugreport and screenshot files should be empty when user consent timed out.
+ assertThat(mBugreportFile.length()).isEqualTo(0);
+ assertThat(mScreenshotFile.length()).isEqualTo(0);
+ assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+ }
+
@Test
public void simultaneousBugreportsNotAllowed() throws Exception {
// Start bugreport #1
@@ -129,9 +148,10 @@ public class BugreportManagerTest {
// Before #1 is done, try to start #2.
assertThat(callback.isDone()).isFalse();
BugreportCallbackImpl callback2 = new BugreportCallbackImpl();
- ParcelFileDescriptor bugreportFd2 = parcelFd("bugreport_2_" + name.getMethodName(), ".zip");
- ParcelFileDescriptor screenshotFd2 =
- parcelFd("screenshot_2_" + name.getMethodName(), ".png");
+ File bugreportFile2 = createTempFile("bugreport_2_" + name.getMethodName(), ".zip");
+ File screenshotFile2 = createTempFile("screenshot_2_" + name.getMethodName(), ".png");
+ ParcelFileDescriptor bugreportFd2 = parcelFd(bugreportFile2);
+ ParcelFileDescriptor screenshotFd2 = parcelFd(screenshotFile2);
mBrm.startBugreport(bugreportFd2, screenshotFd2, wifi(), mExecutor, callback2);
Thread.sleep(500 /* .5s */);
@@ -271,12 +291,16 @@ public class BugreportManagerTest {
return bm;
}
- private static ParcelFileDescriptor parcelFd(String prefix, String extension) throws Exception {
- File f = File.createTempFile(prefix, extension);
+ private static File createTempFile(String prefix, String extension) throws Exception {
+ final File f = File.createTempFile(prefix, extension);
f.setReadable(true, true);
f.setWritable(true, true);
+ f.deleteOnExit();
+ return f;
+ }
- return ParcelFileDescriptor.open(f,
+ private static ParcelFileDescriptor parcelFd(File file) throws Exception {
+ return ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
}
@@ -342,4 +366,13 @@ public class BugreportManagerTest {
private static BugreportParams interactive() {
return new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE);
}
+
+ /*
+ * Returns a {@link BugreportParams} for full bugreport that includes a screenshot.
+ *
+ *
This can take on the order of minutes to finish
+ */
+ private static BugreportParams full() {
+ return new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL);
+ }
}
diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v1.apk
similarity index 73%
rename from core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch.apk
rename to core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v1.apk
index 1dc1e998f1eec74997b607ad755b70f4ad1a7a84..add4aa0387083adeff1a37e73ecef42c7a8cddbc 100644
Binary files a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch.apk and b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v1.apk differ
diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v2.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v2.apk
new file mode 100644
index 0000000000000000000000000000000000000000..e55eb903c68bbe13fc3f240aef44f8245f1bd271
Binary files /dev/null and b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v2.apk differ
diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v3.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v3.apk
new file mode 100644
index 0000000000000000000000000000000000000000..de23558220939d4377f11bf5730863328b1a7ef3
Binary files /dev/null and b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v3.apk differ
diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-certificate-mismatch.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-certificate-mismatch.apk
index 562805cf67eb0effef39d2e6a6980a4910911e06..f1105f96664c919203ddd481e77cba6937a0acb5 100644
Binary files a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-certificate-mismatch.apk and b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-certificate-mismatch.apk differ
diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-malformed-signature.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-malformed-signature.apk
index 2723cc8fdbeb53af3f86d35471ddd0ef5885f89e..d28774aa2b1427eb2731950f4a705d93c0144ba3 100644
Binary files a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-malformed-signature.apk and b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-malformed-signature.apk differ
diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-without-block.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-without-block.apk
index 9dec2f5911334989b135a135e6858c008e0afaf6..604fe6f43523732bbb080160d8e3c6f7c8b2585e 100644
Binary files a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-without-block.apk and b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-without-block.apk differ
diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/valid-stamp.apk b/core/tests/coretests/assets/SourceStampVerifierTest/valid-stamp.apk
index 8056e0bf6e50d06eee7fa657e08b3a02f6e40e64..2f2a5923d24aad2d8c046025ff9fffa07f9e8734 100644
Binary files a/core/tests/coretests/assets/SourceStampVerifierTest/valid-stamp.apk and b/core/tests/coretests/assets/SourceStampVerifierTest/valid-stamp.apk differ
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index f13a11e9edfba2e4fd9a9e3622ccca3b8eeb337b..777f4a3e03a8ba2872cca9522fbc651093ecbeca 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -23,12 +23,15 @@ import static android.view.Display.DEFAULT_DISPLAY;
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.assertTrue;
import android.app.ActivityThread;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
+import android.inputmethodservice.InputMethodService;
import android.media.ImageReader;
import android.os.UserHandle;
import android.view.Display;
@@ -135,6 +138,13 @@ public class ContextTest {
assertThat(systemUiContext.isUiContext()).isTrue();
}
+ @Test
+ public void testIsUiContext_InputMethodService_returnsTrue() {
+ final InputMethodService ims = new InputMethodService();
+
+ assertTrue(ims.isUiContext());
+ }
+
@Test
public void testGetDisplayFromDisplayContextDerivedContextOnPrimaryDisplay() {
verifyGetDisplayFromDisplayContextDerivedContext(false /* onSecondaryDisplay */);
@@ -171,4 +181,26 @@ public class ContextTest {
VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
return virtualDisplay.getDisplay();
}
+
+ @Test
+ public void testIsUiContext_ContextWrapper() {
+ ContextWrapper wrapper = new ContextWrapper(null /* base */);
+
+ assertFalse(wrapper.isUiContext());
+
+ wrapper = new ContextWrapper(new TestUiContext());
+
+ assertTrue(wrapper.isUiContext());
+ }
+
+ private static class TestUiContext extends ContextWrapper {
+ TestUiContext() {
+ super(null /* base */);
+ }
+
+ @Override
+ public boolean isUiContext() {
+ return true;
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
index 37b2817928aa6248cea2bc37f1b11cc8a0645539..81d54b57486cad40126f79e5cd3f842ec1305174 100644
--- a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
+++ b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
@@ -26,8 +26,11 @@ import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import android.content.Context;
-import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
+import libcore.io.Streams;
+
+import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -46,8 +49,20 @@ import java.util.zip.ZipFile;
@RunWith(JUnit4.class)
public class SourceStampVerifierTest {
- private final Context mContext =
- InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ private File mPrimaryApk;
+ private File mSecondaryApk;
+
+ @After
+ public void tearDown() throws Exception {
+ if (mPrimaryApk != null) {
+ mPrimaryApk.delete();
+ }
+ if (mSecondaryApk != null) {
+ mSecondaryApk.delete();
+ }
+ }
@Test
public void testSourceStamp_noStamp() throws Exception {
@@ -63,17 +78,11 @@ public class SourceStampVerifierTest {
@Test
public void testSourceStamp_correctSignature() throws Exception {
- File testApk = getApk("SourceStampVerifierTest/valid-stamp.apk");
- ZipFile apkZipFile = new ZipFile(testApk);
- ZipEntry stampCertZipEntry = apkZipFile.getEntry("stamp-cert-sha256");
- int size = (int) stampCertZipEntry.getSize();
- byte[] expectedStampCertHash = new byte[size];
- try (InputStream inputStream = apkZipFile.getInputStream(stampCertZipEntry)) {
- inputStream.read(expectedStampCertHash);
- }
+ mPrimaryApk = getApk("SourceStampVerifierTest/valid-stamp.apk");
+ byte[] expectedStampCertHash = getSourceStampCertificateHashFromApk(mPrimaryApk);
SourceStampVerificationResult result =
- SourceStampVerifier.verify(testApk.getAbsolutePath());
+ SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath());
assertTrue(result.isPresent());
assertTrue(result.isVerified());
@@ -85,10 +94,10 @@ public class SourceStampVerifierTest {
@Test
public void testSourceStamp_signatureMissing() throws Exception {
- File testApk = getApk("SourceStampVerifierTest/stamp-without-block.apk");
+ mPrimaryApk = getApk("SourceStampVerifierTest/stamp-without-block.apk");
SourceStampVerificationResult result =
- SourceStampVerifier.verify(testApk.getAbsolutePath());
+ SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath());
assertTrue(result.isPresent());
assertFalse(result.isVerified());
@@ -97,10 +106,10 @@ public class SourceStampVerifierTest {
@Test
public void testSourceStamp_certificateMismatch() throws Exception {
- File testApk = getApk("SourceStampVerifierTest/stamp-certificate-mismatch.apk");
+ mPrimaryApk = getApk("SourceStampVerifierTest/stamp-certificate-mismatch.apk");
SourceStampVerificationResult result =
- SourceStampVerifier.verify(testApk.getAbsolutePath());
+ SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath());
assertTrue(result.isPresent());
assertFalse(result.isVerified());
@@ -108,11 +117,35 @@ public class SourceStampVerifierTest {
}
@Test
- public void testSourceStamp_apkHashMismatch() throws Exception {
- File testApk = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch.apk");
+ public void testSourceStamp_apkHashMismatch_v1SignatureScheme() throws Exception {
+ mPrimaryApk = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch-v1.apk");
SourceStampVerificationResult result =
- SourceStampVerifier.verify(testApk.getAbsolutePath());
+ SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath());
+
+ assertTrue(result.isPresent());
+ assertFalse(result.isVerified());
+ assertNull(result.getCertificate());
+ }
+
+ @Test
+ public void testSourceStamp_apkHashMismatch_v2SignatureScheme() throws Exception {
+ mPrimaryApk = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch-v2.apk");
+
+ SourceStampVerificationResult result =
+ SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath());
+
+ assertTrue(result.isPresent());
+ assertFalse(result.isVerified());
+ assertNull(result.getCertificate());
+ }
+
+ @Test
+ public void testSourceStamp_apkHashMismatch_v3SignatureScheme() throws Exception {
+ mPrimaryApk = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch-v3.apk");
+
+ SourceStampVerificationResult result =
+ SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath());
assertTrue(result.isPresent());
assertFalse(result.isVerified());
@@ -121,10 +154,10 @@ public class SourceStampVerifierTest {
@Test
public void testSourceStamp_malformedSignature() throws Exception {
- File testApk = getApk("SourceStampVerifierTest/stamp-malformed-signature.apk");
+ mPrimaryApk = getApk("SourceStampVerifierTest/stamp-malformed-signature.apk");
SourceStampVerificationResult result =
- SourceStampVerifier.verify(testApk.getAbsolutePath());
+ SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath());
assertTrue(result.isPresent());
assertFalse(result.isVerified());
@@ -133,21 +166,14 @@ public class SourceStampVerifierTest {
@Test
public void testSourceStamp_multiApk_validStamps() throws Exception {
- File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk");
- File testApk2 = getApk("SourceStampVerifierTest/valid-stamp.apk");
- ZipFile apkZipFile = new ZipFile(testApk1);
- ZipEntry stampCertZipEntry = apkZipFile.getEntry("stamp-cert-sha256");
- int size = (int) stampCertZipEntry.getSize();
- byte[] expectedStampCertHash = new byte[size];
- try (InputStream inputStream = apkZipFile.getInputStream(stampCertZipEntry)) {
- inputStream.read(expectedStampCertHash);
- }
+ mPrimaryApk = getApk("SourceStampVerifierTest/valid-stamp.apk");
+ mSecondaryApk = getApk("SourceStampVerifierTest/valid-stamp.apk");
+ byte[] expectedStampCertHash = getSourceStampCertificateHashFromApk(mPrimaryApk);
List apkFiles = new ArrayList<>();
- apkFiles.add(testApk1.getAbsolutePath());
- apkFiles.add(testApk2.getAbsolutePath());
+ apkFiles.add(mPrimaryApk.getAbsolutePath());
+ apkFiles.add(mSecondaryApk.getAbsolutePath());
- SourceStampVerificationResult result =
- SourceStampVerifier.verify(apkFiles);
+ SourceStampVerificationResult result = SourceStampVerifier.verify(apkFiles);
assertTrue(result.isPresent());
assertTrue(result.isVerified());
@@ -159,14 +185,13 @@ public class SourceStampVerifierTest {
@Test
public void testSourceStamp_multiApk_invalidStamps() throws Exception {
- File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk");
- File testApk2 = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch.apk");
+ mPrimaryApk = getApk("SourceStampVerifierTest/valid-stamp.apk");
+ mSecondaryApk = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch-v3.apk");
List apkFiles = new ArrayList<>();
- apkFiles.add(testApk1.getAbsolutePath());
- apkFiles.add(testApk2.getAbsolutePath());
+ apkFiles.add(mPrimaryApk.getAbsolutePath());
+ apkFiles.add(mSecondaryApk.getAbsolutePath());
- SourceStampVerificationResult result =
- SourceStampVerifier.verify(apkFiles);
+ SourceStampVerificationResult result = SourceStampVerifier.verify(apkFiles);
assertTrue(result.isPresent());
assertFalse(result.isVerified());
@@ -174,10 +199,16 @@ public class SourceStampVerifierTest {
}
private File getApk(String apkPath) throws IOException {
- File testApk = File.createTempFile("SourceStampApk", ".apk");
+ File apk = File.createTempFile("SourceStampApk", ".apk");
try (InputStream inputStream = mContext.getAssets().open(apkPath)) {
- Files.copy(inputStream, testApk.toPath(), REPLACE_EXISTING);
+ Files.copy(inputStream, apk.toPath(), REPLACE_EXISTING);
}
- return testApk;
+ return apk;
+ }
+
+ private byte[] getSourceStampCertificateHashFromApk(File apk) throws IOException {
+ ZipFile apkZipFile = new ZipFile(apk);
+ ZipEntry stampCertZipEntry = apkZipFile.getEntry("stamp-cert-sha256");
+ return Streams.readFully(apkZipFile.getInputStream(stampCertZipEntry));
}
}
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 8eca650398bf2aea364c6a26b7ecac161db86f2f..a2b1e3d69cd2edb03509efe3c227ecd8261e78f0 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -234,6 +234,24 @@ public class InsetsAnimationControlImplTest {
verify(mMockListener).onFinished(mController);
}
+ @Test
+ public void testPerceptible_insets() {
+ mController.setInsetsAndAlpha(mController.getHiddenStateInsets(), 1f, 1f);
+ verify(mMockController).reportPerceptible(systemBars(), false);
+
+ mController.setInsetsAndAlpha(mController.getShownStateInsets(), 1f, 1f);
+ verify(mMockController).reportPerceptible(systemBars(), true);
+ }
+
+ @Test
+ public void testPerceptible_alpha() {
+ mController.setInsetsAndAlpha(mController.getShownStateInsets(), 0f, 1f);
+ verify(mMockController).reportPerceptible(systemBars(), false);
+
+ mController.setInsetsAndAlpha(mController.getShownStateInsets(), 1f, 1f);
+ verify(mMockController).reportPerceptible(systemBars(), true);
+ }
+
private void assertPosition(Matrix m, Rect original, Rect transformed) {
RectF rect = new RectF(original);
rect.offsetTo(0, 0);
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index d4c256972b28a33be42c7db40f8c46c34efed6cf..c36f1067149ef583d301be9fb4258140994a1ef4 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -678,7 +678,8 @@ public class InsetsControllerTest {
final InsetsState currentState = new InsetsState(mController.getState());
// The caption bar source should be synced with the info in mAttachInfo.
assertEquals(captionFrame, currentState.peekSource(ITYPE_CAPTION_BAR).getFrame());
- assertTrue(currentState.equals(state, true /* excludingCaptionInsets*/));
+ assertTrue(currentState.equals(state, true /* excludingCaptionInsets*/,
+ true /* excludeInvisibleIme */));
mController.setCaptionInsetsHeight(0);
mController.onStateChanged(state);
// The caption bar source should not be there at all, because we don't add empty
@@ -690,18 +691,57 @@ public class InsetsControllerTest {
@Test
public void testRequestedState() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+
+ // The modified state can be controlled when we have control.
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
mController.hide(statusBars());
assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
- mController.onControlsChanged(new InsetsSourceControl[0]);
+
+ // The modified state won't be changed while losing control.
+ mController.onControlsChanged(null /* activeControls */);
assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+
+ // The modified state won't be changed while state changed while we don't have control.
InsetsState newState = new InsetsState(mController.getState(), true /* copySource */);
mController.onStateChanged(newState);
assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+
+ // The modified state won't be changed while controlling an insets without having the
+ // control.
mController.show(statusBars());
assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+
+ // The modified state can be updated while gaining control.
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
assertTrue(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+
+ // The modified state can still be updated if the local state and the requested state
+ // are the same.
+ mController.onControlsChanged(null /* activeControls */);
+ mController.hide(statusBars());
+ newState = new InsetsState(mController.getState(), true /* copySource */);
+ newState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+ mController.onStateChanged(newState);
+ mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
+ assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible());
+
+ // The modified state will always be updated while receiving IME control if IME is
+ // requested visible.
+ mController.getSourceConsumer(ITYPE_IME).show(false /* fromIme */);
+ newState = new InsetsState(mController.getState(), true /* copySource */);
+ newState.getSource(ITYPE_IME).setVisible(true);
+ newState.getSource(ITYPE_IME).setFrame(1, 2, 3, 4);
+ mController.onStateChanged(newState);
+ mController.onControlsChanged(createSingletonControl(ITYPE_IME));
+ assertEquals(newState.getSource(ITYPE_IME),
+ mTestHost.getModifiedState().peekSource(ITYPE_IME));
+ newState = new InsetsState(mController.getState(), true /* copySource */);
+ newState.getSource(ITYPE_IME).setVisible(true);
+ newState.getSource(ITYPE_IME).setFrame(5, 6, 7, 8);
+ mController.onStateChanged(newState);
+ mController.onControlsChanged(createSingletonControl(ITYPE_IME));
+ assertEquals(newState.getSource(ITYPE_IME),
+ mTestHost.getModifiedState().peekSource(ITYPE_IME));
});
}
@@ -716,7 +756,7 @@ public class InsetsControllerTest {
// Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will
// attempt to release mLeash directly.
- SurfaceControl copy = new SurfaceControl(mLeash);
+ SurfaceControl copy = new SurfaceControl(mLeash, "InsetsControllerTest.createControl");
return new InsetsSourceControl(type, copy, new Point());
}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 25f94131f8200c267ef7f96251fed833ab72da83..7efd616c560764ad8d7eb3e54af40726a3d737d5 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -16,6 +16,9 @@
package android.view;
+import static android.view.InsetsController.ANIMATION_TYPE_NONE;
+import static android.view.InsetsController.ANIMATION_TYPE_USER;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.WindowInsets.Type.statusBars;
@@ -24,6 +27,7 @@ import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -31,6 +35,7 @@ import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Point;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager.BadTokenException;
@@ -66,6 +71,9 @@ public class InsetsSourceConsumerTest {
private SurfaceControl mLeash;
@Mock Transaction mMockTransaction;
private InsetsSource mSpyInsetsSource;
+ private boolean mRemoveSurfaceCalled = false;
+ private InsetsController mController;
+ private InsetsState mState;
@Before
public void setup() {
@@ -84,13 +92,19 @@ public class InsetsSourceConsumerTest {
} catch (BadTokenException e) {
// activity isn't running, lets ignore BadTokenException.
}
- InsetsState state = new InsetsState();
+ mState = new InsetsState();
mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR));
- state.addSource(mSpyInsetsSource);
-
- mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, state,
- () -> mMockTransaction,
- new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl)));
+ mState.addSource(mSpyInsetsSource);
+
+ mController = new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl));
+ mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState,
+ () -> mMockTransaction, mController) {
+ @Override
+ public void removeSurface() {
+ super.removeSurface();
+ mRemoveSurfaceCalled = true;
+ }
+ };
});
instrumentation.waitForIdleSync();
@@ -121,6 +135,40 @@ public class InsetsSourceConsumerTest {
}
+ @Test
+ public void testPendingStates() {
+ InsetsState state = new InsetsState();
+ InsetsController controller = mock(InsetsController.class);
+ InsetsSourceConsumer consumer = new InsetsSourceConsumer(
+ ITYPE_IME, state, null, controller);
+
+ InsetsSource source = new InsetsSource(ITYPE_IME);
+ source.setFrame(0, 1, 2, 3);
+ consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_NONE);
+
+ // While we're animating, updates are delayed
+ source.setFrame(4, 5, 6, 7);
+ consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);
+ assertEquals(new Rect(0, 1, 2, 3), state.peekSource(ITYPE_IME).getFrame());
+
+ // Finish the animation, now the pending frame should be applied
+ assertTrue(consumer.notifyAnimationFinished());
+ assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
+
+ // Animating again, updates are delayed
+ source.setFrame(8, 9, 10, 11);
+ consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);
+ assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
+
+ // Updating with the current frame triggers a different code path, verify this clears
+ // the pending 8, 9, 10, 11 frame:
+ source.setFrame(4, 5, 6, 7);
+ consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);
+
+ assertFalse(consumer.notifyAnimationFinished());
+ assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
+ }
+
@Test
public void testRestore() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
@@ -132,6 +180,25 @@ public class InsetsSourceConsumerTest {
mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()),
new int[1], hideTypes);
assertEquals(statusBars(), hideTypes[0]);
+ assertFalse(mRemoveSurfaceCalled);
});
}
+
+ @Test
+ public void testRestore_noAnimation() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mConsumer.hide();
+ mController.onStateChanged(mState);
+ mConsumer.setControl(null, new int[1], new int[1]);
+ reset(mMockTransaction);
+ verifyZeroInteractions(mMockTransaction);
+ mRemoveSurfaceCalled = false;
+ int[] hideTypes = new int[1];
+ mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()),
+ new int[1], hideTypes);
+ assertTrue(mRemoveSurfaceCalled);
+ assertEquals(0, hideTypes[0]);
+ });
+
+ }
}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index daaf31a6bb65bd8677dd4fbb0213fd8a3c53ef01..5260ef83cc4f755591f7bc829b5d454a1f053a8d 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -27,6 +27,8 @@ import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
@@ -273,6 +275,15 @@ public class InsetsStateTest {
assertEqualsAndHashCode();
}
+ @Test
+ public void testEquals_excludeInvisibleIme() {
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_IME).setVisible(false);
+ mState2.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 200));
+ mState2.getSource(ITYPE_IME).setVisible(false);
+ assertTrue(mState2.equals(mState, true, true /* excludeInvisibleIme */));
+ }
+
@Test
public void testParcelUnparcel() {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 100));
@@ -293,6 +304,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setVisibleFrame(new Rect(0, 0, 50, 10));
mState.getSource(ITYPE_IME).setVisible(true);
mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState2.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(0, 0, 100, 100));
mState2.set(mState, true);
assertEquals(mState, mState2);
}
@@ -341,6 +353,26 @@ public class InsetsStateTest {
}
}
+ @Test
+ public void testCalculateUncontrollableInsets() throws Exception {
+ try (InsetsModeSession session = new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 200, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 200, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+ mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(100, 0, 200, 300));
+ mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
+
+ mState.setDisplayFrame(new Rect(0, 0, 200, 300));
+ assertEquals(0,
+ mState.calculateUncontrollableInsetsFromFrame(new Rect(0, 0, 200, 300)));
+ assertEquals(statusBars() | ime(),
+ mState.calculateUncontrollableInsetsFromFrame(new Rect(0, 50, 200, 250)));
+ assertEquals(navigationBars(),
+ mState.calculateUncontrollableInsetsFromFrame(new Rect(50, 0, 150, 300)));
+ }
+ }
+
private void assertEqualsAndHashCode() {
assertEquals(mState, mState2);
assertEquals(mState.hashCode(), mState2.hashCode());
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
index a8ca6f048a1167c228c2275c8a4343fa22d144f4..b329e55b569f793627602bc0aee7cd668d75f431 100644
--- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
+++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
@@ -125,6 +125,32 @@ public class AutofillIdTest {
assertNonVirtual(idWithoutSession, 42, NO_SESSION);
}
+ @Test
+ public void testVirtual_Long_withoutSession() {
+ final AutofillId id = new AutofillId(new AutofillId(42), 108L, 666);
+ final AutofillId idWithoutSession = AutofillId.withoutSession(id);
+ assertThat(idWithoutSession.getViewId()).isEqualTo(42);
+ assertThat(idWithoutSession.isVirtualLong()).isTrue();
+ assertThat(idWithoutSession.isVirtualInt()).isFalse();
+ assertThat(idWithoutSession.isNonVirtual()).isFalse();
+ assertThat(idWithoutSession.getVirtualChildLongId()).isEqualTo(108L);
+ assertThat(idWithoutSession.getVirtualChildIntId()).isEqualTo(View.NO_ID);
+ assertThat(idWithoutSession.getSessionId()).isEqualTo(NO_SESSION);
+ }
+
+ @Test
+ public void testVirtual_Int_withoutSession() {
+ final AutofillId id = new AutofillId(42, 108);
+ final AutofillId idWithoutSession = AutofillId.withoutSession(id);
+ assertThat(idWithoutSession.getViewId()).isEqualTo(42);
+ assertThat(idWithoutSession.isVirtualLong()).isFalse();
+ assertThat(idWithoutSession.isVirtualInt()).isTrue();
+ assertThat(idWithoutSession.isNonVirtual()).isFalse();
+ assertThat(idWithoutSession.getVirtualChildIntId()).isEqualTo(108);
+ assertThat(idWithoutSession.getVirtualChildLongId()).isEqualTo(View.NO_ID);
+ assertThat(idWithoutSession.getSessionId()).isEqualTo(NO_SESSION);
+ }
+
@Test
public void testSetResetSession() {
final AutofillId id = new AutofillId(42);
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index 89cc6e743752c1bec37609d86472932e1c50b16c..df2946c97d20bd8d8f0ffda20ed88496dd16d055 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -37,7 +37,6 @@ import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.app.Instrumentation;
import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
@@ -96,7 +95,6 @@ public class EditorCursorDragTest {
mMotionEvents.clear();
}
- @Presubmit
@Test
public void testCursorDrag_horizontal_whenTextViewContentsFitOnScreen() throws Throwable {
String text = "Hello world!";
@@ -145,7 +143,7 @@ public class EditorCursorDragTest {
// Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to
// the handle as the touch moves downwards (and because we have some slop to avoid jumping
// across lines), the cursor position will end up higher than the finger position.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1")));
// Swipe right-down along a very steep diagonal path. This should not drag the cursor.
@@ -181,7 +179,7 @@ public class EditorCursorDragTest {
// Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to
// the handle as the touch moves downwards (and because we have some slop to avoid jumping
// across lines), the cursor position will end up higher than the finger position.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1")));
// Swipe right-down along a very steep diagonal path. This should not drag the cursor.
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index ec75e40f13347f0eb961e9d73aa8877e4b1bc8c7..35fd4bd7dc14aa217446d9605dc4d38cf4b8f454 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -326,9 +326,9 @@ public class EditorTouchStateTest {
mTouchState.update(event1, mConfig);
assertSingleTap(mTouchState, 0f, 0f, 0, 0);
- // Simulate an ACTION_MOVE event that is > 30 deg from vertical.
+ // Simulate an ACTION_MOVE event that is > 45 deg from vertical.
long event2Time = 1002;
- MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 173f);
+ MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f);
mTouchState.update(event2, mConfig);
assertDrag(mTouchState, 0f, 0f, 0, 0, false);
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 dcecb5f32096ba05250b87b18fe99e0f744e7d4f..090645f6f7a8338ca240edeb3272434d05c256c1 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -21,6 +21,7 @@ import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.hasSibling;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
@@ -34,9 +35,11 @@ import static com.android.internal.app.ChooserListAdapter.SHORTCUT_TARGET_SCORE_
import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
import static com.android.internal.app.MatcherUtils.first;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -297,9 +300,8 @@ public class ChooserActivityTest {
public void fourOptionsStackedIntoOneTarget() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
- // create 12 unique app targets to ensure the app ranking row can be filled, otherwise
- // targets will not stack
- List resolvedComponentInfos = createResolvedComponentsForTest(12);
+ // create just enough targets to ensure the a-z list should be shown
+ List resolvedComponentInfos = createResolvedComponentsForTest(1);
// next create 4 targets in a single app that should be stacked into a single target
String packageName = "xxx.yyy";
@@ -326,8 +328,8 @@ public class ChooserActivityTest {
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- // expect 12 unique targets + 1 group + 4 ranked app targets
- assertThat(activity.getAdapter().getCount(), is(17));
+ // expect 1 unique targets + 1 group + 4 ranked app targets
+ assertThat(activity.getAdapter().getCount(), is(6));
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
@@ -335,7 +337,7 @@ public class ChooserActivityTest {
return true;
};
- onView(withText(appName)).perform(click());
+ onView(allOf(withText(appName), hasSibling(withText("")))).perform(click());
waitForIdle();
// clicking will launch a dialog to choose the activity within the app
@@ -1327,7 +1329,6 @@ public class ChooserActivityTest {
assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
}
- @Ignore // b/148156663
@Test
public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() throws InterruptedException {
// enable the work tab feature flag
@@ -1354,8 +1355,10 @@ public class ChooserActivityTest {
// wait for the share sheet to expand
Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
- onView(first(withText(workResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name)))
+ onView(first(allOf(
+ withText(workResolvedComponentInfos.get(0)
+ .getResolveInfoAt(0).activityInfo.applicationInfo.name),
+ isDisplayed())))
.perform(click());
waitForIdle();
assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
@@ -1953,6 +1956,173 @@ public class ChooserActivityTest {
assertThat(activity.getAdapter().getRankedTargetCount(), is(3));
}
+ @Test
+ public void testWorkTab_selectingWorkTabWithPausedWorkProfile_directShareTargetsNotQueried() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List workResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ sOverrides.isQuietModeEnabled = true;
+ boolean[] isQueryDirectShareCalledOnWorkProfile = new boolean[] { false };
+ sOverrides.onQueryDirectShareTargets = chooserListAdapter -> {
+ isQueryDirectShareCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
+ boolean[] isQueryTargetServicesCalledOnWorkProfile = new boolean[] { false };
+ sOverrides.onQueryTargetServices = chooserListAdapter -> {
+ isQueryTargetServicesCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ assertFalse("Direct share targets were queried on a paused work profile",
+ isQueryDirectShareCalledOnWorkProfile[0]);
+ assertFalse("Target services were queried on a paused work profile",
+ isQueryTargetServicesCalledOnWorkProfile[0]);
+ }
+
+ @Test
+ public void testWorkTab_selectingWorkTabWithNotRunningWorkUser_directShareTargetsNotQueried() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List workResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ sOverrides.isWorkProfileUserRunning = false;
+ boolean[] isQueryDirectShareCalledOnWorkProfile = new boolean[] { false };
+ sOverrides.onQueryDirectShareTargets = chooserListAdapter -> {
+ isQueryDirectShareCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
+ boolean[] isQueryTargetServicesCalledOnWorkProfile = new boolean[] { false };
+ sOverrides.onQueryTargetServices = chooserListAdapter -> {
+ isQueryTargetServicesCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ assertFalse("Direct share targets were queried on a locked work profile user",
+ isQueryDirectShareCalledOnWorkProfile[0]);
+ assertFalse("Target services were queried on a locked work profile user",
+ isQueryTargetServicesCalledOnWorkProfile[0]);
+ }
+
+ @Test
+ public void testWorkTab_workUserNotRunning_workTargetsShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List workResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+ sOverrides.isWorkProfileUserRunning = false;
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ assertEquals(3, activity.getWorkListAdapter().getCount());
+ }
+
+ @Test
+ public void testWorkTab_selectingWorkTabWithLockedWorkUser_directShareTargetsNotQueried() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List workResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ sOverrides.isWorkProfileUserUnlocked = false;
+ boolean[] isQueryDirectShareCalledOnWorkProfile = new boolean[] { false };
+ sOverrides.onQueryDirectShareTargets = chooserListAdapter -> {
+ isQueryDirectShareCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
+ boolean[] isQueryTargetServicesCalledOnWorkProfile = new boolean[] { false };
+ sOverrides.onQueryTargetServices = chooserListAdapter -> {
+ isQueryTargetServicesCalledOnWorkProfile[0] =
+ (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+ return null;
+ };
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ assertFalse("Direct share targets were queried on a locked work profile user",
+ isQueryDirectShareCalledOnWorkProfile[0]);
+ assertFalse("Target services were queried on a locked work profile user",
+ isQueryTargetServicesCalledOnWorkProfile[0]);
+ }
+
+ @Test
+ public void testWorkTab_workUserLocked_workTargetsShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List workResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+ sOverrides.isWorkProfileUserUnlocked = false;
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ assertEquals(3, activity.getWorkListAdapter().getCount());
+ }
+
private Intent createChooserIntent(Intent intent, Intent[] initialIntents) {
Intent chooserIntent = new Intent();
chooserIntent.setAction(Intent.ACTION_CHOOSER);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 749b0e54b8807c8eb0bae80da4f01b2dfc69d095..d3d5caf3f7e20904bc076aae1ea679a8190978e3 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -205,6 +205,44 @@ public class ChooserWrapperActivity extends ChooserActivity {
return getApplicationContext();
}
+ @Override
+ protected void queryDirectShareTargets(ChooserListAdapter adapter,
+ boolean skipAppPredictionService) {
+ if (sOverrides.onQueryDirectShareTargets != null) {
+ sOverrides.onQueryDirectShareTargets.apply(adapter);
+ }
+ super.queryDirectShareTargets(adapter, skipAppPredictionService);
+ }
+
+ @Override
+ protected void queryTargetServices(ChooserListAdapter adapter) {
+ if (sOverrides.onQueryTargetServices != null) {
+ sOverrides.onQueryTargetServices.apply(adapter);
+ }
+ super.queryTargetServices(adapter);
+ }
+
+ @Override
+ protected boolean isQuietModeEnabled(UserHandle userHandle) {
+ return sOverrides.isQuietModeEnabled;
+ }
+
+ @Override
+ protected boolean isUserRunning(UserHandle userHandle) {
+ if (userHandle.equals(UserHandle.SYSTEM)) {
+ return super.isUserRunning(userHandle);
+ }
+ return sOverrides.isWorkProfileUserRunning;
+ }
+
+ @Override
+ protected boolean isUserUnlocked(UserHandle userHandle) {
+ if (userHandle.equals(UserHandle.SYSTEM)) {
+ return super.isUserUnlocked(userHandle);
+ }
+ return sOverrides.isWorkProfileUserUnlocked;
+ }
+
/**
* We cannot directly mock the activity created since instrumentation creates it.
*
@@ -214,6 +252,8 @@ public class ChooserWrapperActivity extends ChooserActivity {
@SuppressWarnings("Since15")
public Function createPackageManager;
public Function onSafelyStartCallback;
+ public Function onQueryDirectShareTargets;
+ public Function onQueryTargetServices;
public ResolverListController resolverListController;
public ResolverListController workResolverListController;
public Boolean isVoiceInteraction;
@@ -228,11 +268,15 @@ public class ChooserWrapperActivity extends ChooserActivity {
public UserHandle workProfileUserHandle;
public boolean hasCrossProfileIntents;
public boolean isQuietModeEnabled;
+ public boolean isWorkProfileUserRunning;
+ public boolean isWorkProfileUserUnlocked;
public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
public PackageManager packageManager;
public void reset() {
onSafelyStartCallback = null;
+ onQueryDirectShareTargets = null;
+ onQueryTargetServices = null;
isVoiceInteraction = null;
createPackageManager = null;
previewThumbnail = null;
@@ -248,6 +292,8 @@ public class ChooserWrapperActivity extends ChooserActivity {
workProfileUserHandle = null;
hasCrossProfileIntents = true;
isQuietModeEnabled = false;
+ isWorkProfileUserRunning = true;
+ isWorkProfileUserUnlocked = true;
packageManager = null;
multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
@Override
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 8bee1e5cab3ba8c2acc7a455f263a64d83b37ef3..7dc5a8b58f9149881f468d719bf1ff12f8890876 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -592,7 +592,6 @@ public class ResolverActivityTest {
TextUtils.equals(initialText, currentText));
}
- @Ignore // b/148156663
@Test
public void testWorkTab_noPersonalApps_canStartWorkApps()
throws InterruptedException {
@@ -617,8 +616,10 @@ public class ResolverActivityTest {
waitForIdle();
// wait for the share sheet to expand
Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
- onView(first(allOf(withText(workResolvedComponentInfos.get(0)
- .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
+ onView(first(allOf(
+ withText(workResolvedComponentInfos.get(0)
+ .getResolveInfoAt(0).activityInfo.applicationInfo.name),
+ isDisplayed())))
.perform(click());
onView(withId(R.id.button_once))
.perform(click());
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 7cd2f3b4c2ab2de7b4badf04c9d85db6ce670ae5..a4f2065866256b4f12a761eee2f0ba08db0da460 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -363,6 +363,16 @@ public class HdmiAudioSystemClientTest {
public boolean isHdmiCecVolumeControlEnabled() {
return true;
}
+
+ @Override
+ public void addHdmiCecVolumeControlFeatureListener(
+ IHdmiCecVolumeControlFeatureListener listener) {
+ }
+
+ @Override
+ public void removeHdmiCecVolumeControlFeatureListener(
+ IHdmiCecVolumeControlFeatureListener listener) {
+ }
}
}
diff --git a/core/tests/overlaytests/remount/Android.bp b/core/tests/overlaytests/remount/Android.bp
index 5757cfe75514e961c61fd392c48643f5590d3a07..939334c94312cc3508c660d6f39aeca1f3ace85b 100644
--- a/core/tests/overlaytests/remount/Android.bp
+++ b/core/tests/overlaytests/remount/Android.bp
@@ -19,6 +19,9 @@ java_test_host {
"tradefed",
"junit",
],
+ static_libs: [
+ "frameworks-base-hostutils",
+ ],
test_suites: ["general-tests"],
java_resources: [
":com.android.overlaytest.overlaid",
@@ -28,5 +31,6 @@ java_test_host {
":OverlayRemountedTest_Target",
":OverlayRemountedTest_TargetUpgrade",
":OverlayRemountedTest_Overlay",
+ ":OverlayRemountedTest_Overlay_SameCert",
],
}
diff --git a/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/OverlayRemountedTestBase.java b/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/OverlayRemountedTestBase.java
index 14b5bf6eacbaba648ac7c9c3f9024173df7db0b5..1a39e20f9a2b3e75eac73e4950c595534c9fe756 100644
--- a/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/OverlayRemountedTestBase.java
+++ b/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/OverlayRemountedTestBase.java
@@ -18,6 +18,7 @@ package com.android.overlaytest.remounted;
import static org.junit.Assert.fail;
+import com.android.internal.util.test.SystemPreparer;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
diff --git a/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/PackagedUpgradedTest.java b/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/RegenerateIdmapTest.java
similarity index 66%
rename from core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/PackagedUpgradedTest.java
rename to core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/RegenerateIdmapTest.java
index a4656403b03fd280e87967bd2c9ee7857e95f52a..2b68015536edff32e71d5a446cabfae34aa61113 100644
--- a/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/PackagedUpgradedTest.java
+++ b/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/RegenerateIdmapTest.java
@@ -22,7 +22,9 @@ import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class PackagedUpgradedTest extends OverlayRemountedTestBase {
+public class RegenerateIdmapTest extends OverlayRemountedTestBase {
+ private static final String OVERLAY_SIGNATURE_APK =
+ "OverlayRemountedTest_Overlay_SameCert.apk";
private static final String TARGET_UPGRADE_APK = "OverlayRemountedTest_TargetUpgrade.apk";
@Test
@@ -66,4 +68,32 @@ public class PackagedUpgradedTest extends OverlayRemountedTestBase {
assertResource(targetReference, "@" + 0x7f0100ff + " -> true");
assertResource(targetOverlaid, "true");
}
+
+ @Test
+ public void testIdmapPoliciesChanged() throws Exception {
+ final String targetResource = resourceName(TARGET_PACKAGE, "bool",
+ "signature_policy_overlaid");
+
+ mPreparer.pushResourceFile(TARGET_APK, "/product/app/OverlayTarget.apk")
+ .pushResourceFile(OVERLAY_APK, "/product/overlay/TestOverlay.apk")
+ .reboot()
+ .setOverlayEnabled(OVERLAY_PACKAGE, false);
+
+ assertResource(targetResource, "false");
+
+ // The overlay is not signed with the same signature as the target.
+ mPreparer.setOverlayEnabled(OVERLAY_PACKAGE, true);
+ assertResource(targetResource, "false");
+
+ // Replace the overlay with a version of the overlay that is signed with the same signature
+ // as the target.
+ mPreparer.pushResourceFile(OVERLAY_SIGNATURE_APK, "/product/overlay/TestOverlay.apk")
+ .reboot();
+
+ // The idmap should have been recreated with the signature policy fulfilled.
+ assertResource(targetResource, "true");
+
+ mPreparer.setOverlayEnabled(OVERLAY_PACKAGE, false);
+ assertResource(targetResource, "false");
+ }
}
diff --git a/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/SystemPreparer.java b/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/SystemPreparer.java
deleted file mode 100644
index bb72d0ee1d037214a0503411d2bd9a531996ab8c..0000000000000000000000000000000000000000
--- a/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/SystemPreparer.java
+++ /dev/null
@@ -1,151 +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.overlaytest.remounted;
-
-import static org.junit.Assert.assertTrue;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
-import org.junit.Assert;
-import org.junit.rules.ExternalResource;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-class SystemPreparer extends ExternalResource {
- private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000;
-
- // The paths of the files pushed onto the device through this rule.
- private ArrayList mPushedFiles = new ArrayList<>();
-
- // The package names of packages installed through this rule.
- private ArrayList mInstalledPackages = new ArrayList<>();
-
- private final TemporaryFolder mHostTempFolder;
- private final DeviceProvider mDeviceProvider;
-
- SystemPreparer(TemporaryFolder hostTempFolder, DeviceProvider deviceProvider) {
- mHostTempFolder = hostTempFolder;
- mDeviceProvider = deviceProvider;
- }
-
- /** Copies a file within the host test jar to a path on device. */
- SystemPreparer pushResourceFile(String resourcePath,
- String outputPath) throws DeviceNotAvailableException, IOException {
- final ITestDevice device = mDeviceProvider.getDevice();
- remount();
- assertTrue(device.pushFile(copyResourceToTemp(resourcePath), outputPath));
- mPushedFiles.add(outputPath);
- return this;
- }
-
- /** Installs an APK within the host test jar onto the device. */
- SystemPreparer installResourceApk(String resourcePath, String packageName)
- throws DeviceNotAvailableException, IOException {
- final ITestDevice device = mDeviceProvider.getDevice();
- final File tmpFile = copyResourceToTemp(resourcePath);
- final String result = device.installPackage(tmpFile, true /* reinstall */);
- Assert.assertNull(result);
- mInstalledPackages.add(packageName);
- return this;
- }
-
- /** Sets the enable state of an overlay package. */
- SystemPreparer setOverlayEnabled(String packageName, boolean enabled)
- throws DeviceNotAvailableException {
- final ITestDevice device = mDeviceProvider.getDevice();
- final String enable = enabled ? "enable" : "disable";
-
- // Wait for the overlay to change its enabled state.
- final long endMillis = System.currentTimeMillis() + OVERLAY_ENABLE_TIMEOUT_MS;
- String result;
- while (System.currentTimeMillis() <= endMillis) {
- device.executeShellCommand(String.format("cmd overlay %s %s", enable, packageName));
- result = device.executeShellCommand("cmd overlay dump isenabled "
- + packageName);
- if (((enabled) ? "true\n" : "false\n").equals(result)) {
- return this;
- }
-
- try {
- Thread.sleep(200);
- } catch (InterruptedException ignore) {
- }
- }
-
- throw new IllegalStateException(String.format("Failed to %s overlay %s:\n%s", enable,
- packageName, device.executeShellCommand("cmd overlay list")));
- }
-
- /** Restarts the device and waits until after boot is completed. */
- SystemPreparer reboot() throws DeviceNotAvailableException {
- final ITestDevice device = mDeviceProvider.getDevice();
- device.reboot();
- return this;
- }
-
- SystemPreparer remount() throws DeviceNotAvailableException {
- mDeviceProvider.getDevice().executeAdbCommand("remount");
- return this;
- }
-
- /** Copies a file within the host test jar to a temporary file on the host machine. */
- private File copyResourceToTemp(String resourcePath) throws IOException {
- final File tempFile = mHostTempFolder.newFile(resourcePath);
- final ClassLoader classLoader = getClass().getClassLoader();
- try (InputStream assetIs = classLoader.getResource(resourcePath).openStream();
- FileOutputStream assetOs = new FileOutputStream(tempFile)) {
- if (assetIs == null) {
- throw new IllegalStateException("Failed to find resource " + resourcePath);
- }
-
- int b;
- while ((b = assetIs.read()) >= 0) {
- assetOs.write(b);
- }
- }
-
- return tempFile;
- }
-
- /** Removes installed packages and files that were pushed to the device. */
- @Override
- protected void after() {
- final ITestDevice device = mDeviceProvider.getDevice();
- try {
- remount();
- for (final String file : mPushedFiles) {
- device.deleteFile(file);
- }
- for (final String packageName : mInstalledPackages) {
- device.uninstallPackage(packageName);
- }
- device.reboot();
- } catch (DeviceNotAvailableException e) {
- Assert.fail(e.toString());
- }
- }
-
- interface DeviceProvider {
- ITestDevice getDevice();
- }
-}
diff --git a/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp b/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp
index a1fdbfd3542c920a0974d0a8a45c41b24a691599..032a0cdcb50d4adf0a3a220c7ab3f185239f58be 100644
--- a/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp
@@ -19,3 +19,9 @@ android_test_helper_app {
"com.android.overlaytest.overlay",
],
}
+
+android_test_helper_app {
+ name: "OverlayRemountedTest_Overlay_SameCert",
+ certificate: ":rro-remounted-test-a",
+ sdk_version: "current",
+}
\ No newline at end of file
diff --git a/core/tests/overlaytests/remount/test-apps/Overlay/res/values/values.xml b/core/tests/overlaytests/remount/test-apps/Overlay/res/values/values.xml
index 675e44f19f9512ff76cf62200f5f0f057d2e3252..927d37fc60b9524076730f6cc795e4a2975c395f 100644
--- a/core/tests/overlaytests/remount/test-apps/Overlay/res/values/values.xml
+++ b/core/tests/overlaytests/remount/test-apps/Overlay/res/values/values.xml
@@ -17,4 +17,5 @@
true
+ true
diff --git a/core/tests/overlaytests/remount/test-apps/Target/Android.bp b/core/tests/overlaytests/remount/test-apps/Target/Android.bp
index 19947b1ea51ad8675ba31e05d69094e0a3fc3902..e4b4eaa8d2209c85119bd87909f63de1fc2985d7 100644
--- a/core/tests/overlaytests/remount/test-apps/Target/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/Target/Android.bp
@@ -15,6 +15,7 @@
android_test_helper_app {
name: "OverlayRemountedTest_Target",
sdk_version: "test_current",
+ certificate: ":rro-remounted-test-a",
apex_available: [
"com.android.overlaytest.overlaid",
],
@@ -23,6 +24,7 @@ android_test_helper_app {
android_test_helper_app {
name: "OverlayRemountedTest_TargetUpgrade",
+ certificate: ":rro-remounted-test-a",
resource_dirs: ["res_upgrade"],
sdk_version: "test_current",
}
diff --git a/core/tests/overlaytests/remount/test-apps/Target/res/values/overlayable.xml b/core/tests/overlaytests/remount/test-apps/Target/res/values/overlayable.xml
index 4aa5bcee8f3dff215728920b4004fc0e9b9e468a..79c9a675a6dd4c6878dadc156b736043d4078228 100644
--- a/core/tests/overlaytests/remount/test-apps/Target/res/values/overlayable.xml
+++ b/core/tests/overlaytests/remount/test-apps/Target/res/values/overlayable.xml
@@ -20,5 +20,8 @@
+
+
+
diff --git a/core/tests/overlaytests/remount/test-apps/Target/res/values/values.xml b/core/tests/overlaytests/remount/test-apps/Target/res/values/values.xml
index 76253a95b76634e66e2e59c64ed2aa0a922891d7..64a1683e14bef6ae94e426415af1aa7a6d43e886 100644
--- a/core/tests/overlaytests/remount/test-apps/Target/res/values/values.xml
+++ b/core/tests/overlaytests/remount/test-apps/Target/res/values/values.xml
@@ -23,4 +23,6 @@
false@bool/target_overlaid
+
+ false
diff --git a/core/tests/overlaytests/remount/test-apps/certs/Android.bp b/core/tests/overlaytests/remount/test-apps/certs/Android.bp
new file mode 100644
index 0000000000000000000000000000000000000000..06114efc12493ada0dda56f3560ec30eaea30a65
--- /dev/null
+++ b/core/tests/overlaytests/remount/test-apps/certs/Android.bp
@@ -0,0 +1,19 @@
+// 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.
+
+// development/tools/make_key rro-remounted-test-a '/CN=rro_test_a'
+android_app_certificate {
+ name: "rro-remounted-test-a",
+ certificate: "rro-remounted-test-a",
+}
diff --git a/core/tests/overlaytests/remount/test-apps/certs/rro-remounted-test-a.pk8 b/core/tests/overlaytests/remount/test-apps/certs/rro-remounted-test-a.pk8
new file mode 100644
index 0000000000000000000000000000000000000000..aa6cc97d07f6d000f60721f7fbdf47b2e67a19e3
Binary files /dev/null and b/core/tests/overlaytests/remount/test-apps/certs/rro-remounted-test-a.pk8 differ
diff --git a/core/tests/overlaytests/remount/test-apps/certs/rro-remounted-test-a.x509.pem b/core/tests/overlaytests/remount/test-apps/certs/rro-remounted-test-a.x509.pem
new file mode 100644
index 0000000000000000000000000000000000000000..be491c7029fed0608175c189c78ca341b7b906eb
--- /dev/null
+++ b/core/tests/overlaytests/remount/test-apps/certs/rro-remounted-test-a.x509.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDCzCCAfOgAwIBAgIUfphI+C6W6V6RomsP7+CW5dO5cGcwDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKcnJvX3Rlc3RfYTAeFw0yMDA1MTQxNjM4MDBaFw00NzA5
+MzAxNjM4MDBaMBUxEzARBgNVBAMMCnJyb190ZXN0X2EwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCvXM8tcqQFwH7iQG6+8mAx2ADhwbtq+8Rcmiz7wviW
+Yf/eFDRuvZ/ma6lxeVJ7mcbF7A5rinEKdgN5hlW2UlbTmuX5YOiXnX3Y2J5t+8Pi
+aq787IvWxkawwkj0Oy1Hk01Z4w3HTYntYqi36bq4QyNpwh515VqgvEyCHT7IPtQi
+XjfwcTW0thUlSDyDkgxq9NxNEJgaHHOamKkeMCO8CkBWkhlcPXvjcM8DPFmyzDI9
+Czv8IYFZQbcG/N2GPH9hSteMnuC+zyoMio0V/VRctQGlAA8ATsheBkng0zcNRu9Z
+GIavk5AaClmBFTeQx01j3HFSO8UDdDJ5Hk8uDTqecPLpAgMBAAGjUzBRMB0GA1Ud
+DgQWBBSPbIdzSkPbzltj3qIS13LNDiyIiDAfBgNVHSMEGDAWgBSPbIdzSkPbzltj
+3qIS13LNDiyIiDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCH
+QvvMGyMvVJaWMEJwVdUnszdXiAlUtDd/2HpdGOxW6xVVAvveP8hJ71gWFQ7Qs3Mr
+3jxclbC37qVAPiQb8kkD8qUgoQYMC43asif6Jn65OU1QkDRF3bFHP+rZVSPEwtvl
+YMbOzHPOLr7HESwlM7TB6EoZ4oOso++jTYI/OSif1MOKOMbOt4X/DE/PXf81ayFs
+uRjpocBqnLwOourABMcaKbA92jB0LRTtgv5ngOJ3+5P1cTiHktFbnqVWa8/A3uSA
+dNR5dpOUjH+nCHTwPl64b7R70PgDxnoqMs0xI7VtJovXor64OZy9P8WTdurz5V/z
+k2IVSi032bf0aTxamvqV
+-----END CERTIFICATE-----
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
index fe33cd80f7351da9fb3f3711d716c973fa342ff2..4b8173732b4dd6801de648271fba47f2a03212e9 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
@@ -29,11 +29,12 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.WindowManager;
@@ -91,8 +92,7 @@ public final class ScreenshotHelperTest {
@Test
public void testProvidedImageScreenshot() {
mScreenshotHelper.provideScreenshot(
- Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888), new Rect(),
- Insets.of(0, 0, 0, 0), 1,
+ new Bundle(), new Rect(), Insets.of(0, 0, 0, 0), 1, 1, new ComponentName("", ""),
WindowManager.ScreenshotSource.SCREENSHOT_OTHER, mHandler, null);
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 1d3a3997d8578e7499c8a61e198964a6c35b33d7..fb8b17c1f159b815e82da6e9cbcca27d53350269 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -62,6 +62,14 @@ prebuilt_etc {
filename_from_src: true,
}
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.cellbroadcastreceiver",
+ system_ext_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.cellbroadcastreceiver.xml",
+ filename_from_src: true,
+}
+
prebuilt_etc {
name: "privapp_whitelist_com.android.contacts",
product_specific: true,
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 1b1a624cda5055d3a59605da08a90dc1079250ec..e5492714b29fe780d529015a3fafc84c3b5d17e2 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -121,6 +121,20 @@ prebuilt_etc {
filename_from_src: true,
}
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.bugreport",
+ sub_dir: "permissions",
+ src: "com.android.car.bugreport.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.companiondevicesupport",
+ sub_dir: "permissions",
+ src: "com.android.car.companiondevicesupport.xml",
+ filename_from_src: true,
+}
+
prebuilt_etc {
name: "privapp_whitelist_com.google.android.car.kitchensink",
sub_dir: "permissions",
diff --git a/data/etc/car/com.android.car.bugreport.xml b/data/etc/car/com.android.car.bugreport.xml
new file mode 100644
index 0000000000000000000000000000000000000000..432a838a90e8386023b1ee44bcd5676d2fa1c57f
--- /dev/null
+++ b/data/etc/car/com.android.car.bugreport.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/data/etc/car/com.android.car.companiondevicesupport.xml b/data/etc/car/com.android.car.companiondevicesupport.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2067bab20d3d5e220e3d47ff636afa3c36bed6c8
--- /dev/null
+++ b/data/etc/car/com.android.car.companiondevicesupport.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/data/etc/com.android.cellbroadcastreceiver.xml b/data/etc/com.android.cellbroadcastreceiver.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dd2df42e442f7cf779a7cf4bc88e485dd38d4dcf
--- /dev/null
+++ b/data/etc/com.android.cellbroadcastreceiver.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 17e1f2e0c229fe628d70b916caa8f039fb2acdd1..625558406b9c00fa4bfab6f1f5e1aae5cc27ce7c 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -17,7 +17,9 @@
+
@@ -378,6 +379,9 @@ applications that come with the platform
+
+
+
@@ -418,6 +422,10 @@ applications that come with the platform
+
+
+
+
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index bfc623faeaefbaaf887dd7f63ca9d8452f503d96..67a64ac592f22321bd7fc94c40fe8f79a0d3daf6 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -757,6 +757,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-547111355": {
+ "message": "hideIme Control target: %s ",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"-545190927": {
"message": "<<< CLOSE TRANSACTION animate",
"level": "INFO",
@@ -1087,6 +1093,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "95216706": {
+ "message": "hideIme target: %s ",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"95281111": {
"message": "Attempted to get IME flag of a display that does not exist: %d",
"level": "WARN",
@@ -1663,12 +1675,6 @@
"group": "WM_SHOW_SURFACE_ALLOC",
"at": "com\/android\/server\/wm\/ScreenRotationAnimation.java"
},
- "1108406230": {
- "message": "stopFreezingDisplayLocked: Returning mWaitingForConfig=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d",
- "level": "DEBUG",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"1112047265": {
"message": "finishDrawingWindow: %s mDrawState=%s",
"level": "DEBUG",
@@ -1729,6 +1735,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "1246035185": {
+ "message": "stopFreezingDisplayLocked: Returning waitingForConfig=%b, waitingForRemoteRotation=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"1254403969": {
"message": "Surface returned was null: %s",
"level": "VERBOSE",
@@ -1951,12 +1963,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/AppTransition.java"
},
- "1591969812": {
- "message": "updateImeControlTarget %s",
- "level": "INFO",
- "group": "WM_DEBUG_IME",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"1628345525": {
"message": "Now opening app %s",
"level": "VERBOSE",
diff --git a/data/keyboards/Vendor_045e_Product_0b12.kl b/data/keyboards/Vendor_045e_Product_0b12.kl
new file mode 100644
index 0000000000000000000000000000000000000000..0b44c7434af252aa879f3dd4f63333d4b41badfd
--- /dev/null
+++ b/data/keyboards/Vendor_045e_Product_0b12.kl
@@ -0,0 +1,59 @@
+# 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.
+
+#
+# XBox USB Controller
+#
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+
+# The branded "X" button in the center of the controller
+key 316 BUTTON_MODE
+
+# Three parallel horizontal lines (hamburger menu)
+key 315 BUTTON_START
+
+#Button below the "X" button
+key 167 MEDIA_RECORD
+
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index b351b3d774305ecbadcefbd4188591931cebeca6..4eb6e420c07f25900c89f391c37c5b3bf3027f52 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -41,19 +41,18 @@ public abstract class IdentityCredential {
/**
* Create an ephemeral key pair to use to establish a secure channel with a reader.
*
- *
Most applications will use only the public key, and only to send it to the reader,
- * allowing the private key to be used internally for {@link #encryptMessageToReader(byte[])}
- * and {@link #decryptMessageFromReader(byte[])}. The private key is also provided for
- * applications that wish to use a cipher suite that is not supported by
- * {@link IdentityCredentialStore}.
+ *
Applications should use this key-pair for the communications channel with the reader
+ * using a protocol / cipher-suite appropriate for the application. One example of such a
+ * protocol is the one used for Mobile Driving Licenses, see ISO 18013-5 section 9.2.1 "Session
+ * encryption".
*
* @return ephemeral key pair to use to establish a secure channel with a reader.
*/
public @NonNull abstract KeyPair createEphemeralKeyPair();
/**
- * Set the ephemeral public key provided by the reader. This must be called before
- * {@link #encryptMessageToReader} or {@link #decryptMessageFromReader} can be called.
+ * Set the ephemeral public key provided by the reader. If called, this must be called before
+ * {@link #getEntries(byte[], Map, byte[], byte[])} is called.
*
* @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
* establish a secure session.
@@ -65,6 +64,11 @@ public abstract class IdentityCredential {
/**
* Encrypt a message for transmission to the reader.
*
+ *
Do not use. In this version of the API, this method produces an incorrect
+ * result. Instead, applications should implement message encryption/decryption themselves as
+ * detailed in the {@link #createEphemeralKeyPair()} method. In a future API-level, this
+ * method will be deprecated.
+ *
* @param messagePlaintext unencrypted message to encrypt.
* @return encrypted message.
*/
@@ -73,6 +77,11 @@ public abstract class IdentityCredential {
/**
* Decrypt a message received from the reader.
*
+ *
Do not use. In this version of the API, this method produces an incorrect
+ * result. Instead, applications should implement message encryption/decryption themselves as
+ * detailed in the {@link #createEphemeralKeyPair()} method. In a future API-level, this
+ * method will be deprecated.
+ *
* @param messageCiphertext encrypted message to decrypt.
* @return decrypted message.
* @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
@@ -167,29 +176,18 @@ public abstract class IdentityCredential {
* IntentToRetain = bool
*
*
- *
If the {@code sessionTranscript} parameter is not {@code null}, it must contain CBOR
- * data conforming to the following CDDL schema:
- *
- *
where a {@code COSE_Key} structure for the public part of the key-pair previously
- * generated by {@link #createEphemeralKeyPair()} must appear somewhere in
- * {@code DeviceEngagement} and the X and Y coordinates must both be present
- * in uncompressed form.
+ *
If the {@code sessionTranscript} parameter is not {@code null}, the X and Y coordinates
+ * of the public part of the key-pair previously generated by {@link #createEphemeralKeyPair()}
+ * must appear somewhere in the bytes of the CBOR. Each of these coordinates must appear
+ * encoded with the most significant bits first and use the exact amount of bits indicated by
+ * the key size of the ephemeral keys. For example, if the ephemeral key is using the P-256
+ * curve then the 32 bytes for the X coordinate encoded with the most significant bits first
+ * must appear somewhere in {@code sessionTranscript} and ditto for the 32 bytes for the Y
+ * coordinate.
*
*
If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1}
* structure as defined in RFC 8152. For the payload nil shall be used and the
- * detached payload is the ReaderAuthentication CBOR described below.
+ * detached payload is the ReaderAuthenticationBytes CBOR described below.
*
where {@code ItemsRequestBytes} are the bytes in the {@code requestMessage} parameter.
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
index 37de2c4a50ea54b1a5ba92443f31c389c17054da..71860d261285e8a288e946bd18952f7c34892d62 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -68,8 +68,8 @@ public abstract class ResultData {
* {@link #getMessageAuthenticationCode()} can be used to get a MAC.
*
*
The CBOR structure which is cryptographically authenticated is the
- * {@code DeviceAuthentication} structure according to the following
- * CDDL schema:
+ * {@code DeviceAuthenticationBytes} structure according to the following
+ * CDDL schema:
*
*
where
@@ -115,7 +109,7 @@ public abstract class ResultData {
public abstract @NonNull byte[] getAuthenticatedData();
/**
- * Returns a message authentication code over the {@code DeviceAuthentication} CBOR
+ * Returns a message authentication code over the {@code DeviceAuthenticationBytes} CBOR
* specified in {@link #getAuthenticatedData()}, to prove to the reader that the data
* is from a trusted credential.
*
diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java
index c7aa32855abcca0d01b28c0d812c16219e8da483..305d0ead0652d0a09b273f4ab34790361f7660aa 100644
--- a/identity/java/android/security/identity/WritableIdentityCredential.java
+++ b/identity/java/android/security/identity/WritableIdentityCredential.java
@@ -56,10 +56,10 @@ public abstract class WritableIdentityCredential {
* authority doesn't care about the nature of the security hardware. If called, however, this
* method must be called before {@link #personalize(PersonalizationData)}.
*
- * @param challenge is a byte array whose contents should be unique, fresh and provided by
- * the issuing authority. The value provided is embedded in the attestation
- * extension and enables the issuing authority to verify that the attestation
- * certificate is fresh.
+ * @param challenge is a non-empty byte array whose contents should be unique, fresh and
+ * provided by the issuing authority. The value provided is embedded in the
+ * attestation extension and enables the issuing authority to verify that the
+ * attestation certificate is fresh.
* @return the X.509 certificate for this credential's CredentialKey.
*/
public abstract @NonNull Collection getCredentialKeyCertificateChain(
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 9d0fe11be46b6e80fbd88fd4bec227e4078dbf8f..88b614dc7eef27852b85a3d8e9d9a3b74801421b 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -483,14 +483,14 @@ public class KeyStore {
mBinder.asBinder().linkToDeath(promise, 0);
int errorCode = mBinder.addRngEntropy(promise, data, flags);
if (errorCode == NO_ERROR) {
- return promise.getFuture().get().getErrorCode() == NO_ERROR;
+ return interruptedPreservingGet(promise.getFuture()).getErrorCode() == NO_ERROR;
} else {
return false;
}
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "AddRngEntropy completed with exception", e);
return false;
} finally {
@@ -548,7 +548,7 @@ public class KeyStore {
private int generateKeyInternal(String alias, KeymasterArguments args, byte[] entropy, int uid,
int flags, KeyCharacteristics outCharacteristics)
- throws RemoteException, ExecutionException, InterruptedException {
+ throws RemoteException, ExecutionException {
KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
int error = NO_ERROR;
KeyCharacteristicsCallbackResult result = null;
@@ -559,7 +559,7 @@ public class KeyStore {
Log.e(TAG, "generateKeyInternal failed on request " + error);
return error;
}
- result = promise.getFuture().get();
+ result = interruptedPreservingGet(promise.getFuture());
} finally {
mBinder.asBinder().unlinkToDeath(promise, 0);
}
@@ -592,7 +592,7 @@ public class KeyStore {
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "generateKey completed with exception", e);
return SYSTEM_ERROR;
}
@@ -614,7 +614,7 @@ public class KeyStore {
int error = mBinder.getKeyCharacteristics(promise, alias, clientId, appId, uid);
if (error != NO_ERROR) return error;
- KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+ KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture());
error = result.getKeystoreResponse().getErrorCode();
if (error != NO_ERROR) return error;
@@ -625,7 +625,7 @@ public class KeyStore {
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "GetKeyCharacteristics completed with exception", e);
return SYSTEM_ERROR;
} finally {
@@ -640,14 +640,14 @@ public class KeyStore {
private int importKeyInternal(String alias, KeymasterArguments args, int format, byte[] keyData,
int uid, int flags, KeyCharacteristics outCharacteristics)
- throws RemoteException, ExecutionException, InterruptedException {
+ throws RemoteException, ExecutionException {
KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
mBinder.asBinder().linkToDeath(promise, 0);
try {
int error = mBinder.importKey(promise, alias, args, format, keyData, uid, flags);
if (error != NO_ERROR) return error;
- KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+ KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture());
error = result.getKeystoreResponse().getErrorCode();
if (error != NO_ERROR) return error;
@@ -675,7 +675,7 @@ public class KeyStore {
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "ImportKey completed with exception", e);
return SYSTEM_ERROR;
}
@@ -747,7 +747,7 @@ public class KeyStore {
String wrappingKeyAlias,
byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid,
KeyCharacteristics outCharacteristics)
- throws RemoteException, ExecutionException, InterruptedException {
+ throws RemoteException, ExecutionException {
KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
mBinder.asBinder().linkToDeath(promise, 0);
try {
@@ -755,7 +755,7 @@ public class KeyStore {
wrappingKeyAlias, maskingKey, args, rootSid, fingerprintSid);
if (error != NO_ERROR) return error;
- KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+ KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture());
error = result.getKeystoreResponse().getErrorCode();
if (error != NO_ERROR) return error;
@@ -786,7 +786,7 @@ public class KeyStore {
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "ImportWrappedKey completed with exception", e);
return SYSTEM_ERROR;
}
@@ -818,14 +818,14 @@ public class KeyStore {
appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
int error = mBinder.exportKey(promise, alias, format, clientId, appId, uid);
if (error == NO_ERROR) {
- return promise.getFuture().get();
+ return interruptedPreservingGet(promise.getFuture());
} else {
return new ExportResult(error);
}
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "ExportKey completed with exception", e);
return null;
} finally {
@@ -864,14 +864,14 @@ public class KeyStore {
int errorCode = mBinder.begin(promise, getToken(), alias, purpose, pruneable, args,
entropy, uid);
if (errorCode == NO_ERROR) {
- return promise.getFuture().get();
+ return interruptedPreservingGet(promise.getFuture());
} else {
return new OperationResult(errorCode);
}
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "Begin completed with exception", e);
return null;
} finally {
@@ -894,14 +894,14 @@ public class KeyStore {
input = input != null ? input : new byte[0];
int errorCode = mBinder.update(promise, token, arguments, input);
if (errorCode == NO_ERROR) {
- return promise.getFuture().get();
+ return interruptedPreservingGet(promise.getFuture());
} else {
return new OperationResult(errorCode);
}
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "Update completed with exception", e);
return null;
} finally {
@@ -930,14 +930,14 @@ public class KeyStore {
signature = signature != null ? signature : new byte[0];
int errorCode = mBinder.finish(promise, token, arguments, input, signature, entropy);
if (errorCode == NO_ERROR) {
- return promise.getFuture().get();
+ return interruptedPreservingGet(promise.getFuture());
} else {
return new OperationResult(errorCode);
}
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "Finish completed with exception", e);
return null;
} finally {
@@ -972,14 +972,14 @@ public class KeyStore {
mBinder.asBinder().linkToDeath(promise, 0);
int errorCode = mBinder.abort(promise, token);
if (errorCode == NO_ERROR) {
- return promise.getFuture().get().getErrorCode();
+ return interruptedPreservingGet(promise.getFuture()).getErrorCode();
} else {
return errorCode;
}
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "Abort completed with exception", e);
return SYSTEM_ERROR;
} finally {
@@ -1135,7 +1135,7 @@ public class KeyStore {
}
int error = mBinder.attestKey(promise, alias, params);
if (error != NO_ERROR) return error;
- KeyAttestationCallbackResult result = promise.getFuture().get();
+ KeyAttestationCallbackResult result = interruptedPreservingGet(promise.getFuture());
error = result.getKeystoreResponse().getErrorCode();
if (error == NO_ERROR) {
outChain.shallowCopyFrom(result.getCertificateChain());
@@ -1144,7 +1144,7 @@ public class KeyStore {
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "AttestKey completed with exception", e);
return SYSTEM_ERROR;
} finally {
@@ -1164,7 +1164,7 @@ public class KeyStore {
}
int error = mBinder.attestDeviceIds(promise, params);
if (error != NO_ERROR) return error;
- KeyAttestationCallbackResult result = promise.getFuture().get();
+ KeyAttestationCallbackResult result = interruptedPreservingGet(promise.getFuture());
error = result.getKeystoreResponse().getErrorCode();
if (error == NO_ERROR) {
outChain.shallowCopyFrom(result.getCertificateChain());
@@ -1173,7 +1173,7 @@ public class KeyStore {
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return SYSTEM_ERROR;
- } catch (ExecutionException | InterruptedException e) {
+ } catch (ExecutionException e) {
Log.e(TAG, "AttestDevicdeIds completed with exception", e);
return SYSTEM_ERROR;
} finally {
@@ -1387,4 +1387,20 @@ public class KeyStore {
int errorCode) {
return getInvalidKeyException(keystoreKeyAlias, uid, getKeyStoreException(errorCode));
}
+
+ private static R interruptedPreservingGet(CompletableFuture future)
+ throws ExecutionException {
+ boolean wasInterrupted = false;
+ while (true) {
+ try {
+ R result = future.get();
+ if (wasInterrupted) {
+ Thread.currentThread().interrupt();
+ }
+ return result;
+ } catch (InterruptedException e) {
+ wasInterrupted = true;
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index 308c1a59a7aa3ae3ed5f117184e2c0bb550bacbf..4f4364f72fefedadf3b4aa93cc7c6a3eeb30be52 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -13,26 +13,26 @@
// limitations under the License.
android_library_import {
- name: "window-extensions",
- aars: ["window-extensions-release.aar"],
+ name: "window-sidecar",
+ aars: ["window-sidecar-release.aar"],
sdk_version: "current",
}
java_library {
- name: "androidx.window.extensions",
+ name: "androidx.window.sidecar",
srcs: ["src/**/*.java"],
- static_libs: ["window-extensions"],
+ static_libs: ["window-sidecar"],
installable: true,
sdk_version: "core_platform",
vendor: true,
libs: ["framework", "androidx.annotation_annotation",],
- required: ["androidx.window.extensions.xml",],
+ required: ["androidx.window.sidecar.xml",],
}
prebuilt_etc {
- name: "androidx.window.extensions.xml",
+ name: "androidx.window.sidecar.xml",
vendor: true,
sub_dir: "permissions",
- src: "androidx.window.extensions.xml",
+ src: "androidx.window.sidecar.xml",
filename_from_src: true,
}
diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
deleted file mode 100644
index 1f0ff6656de02ef152a133c5c6e6b40fc8d29e95..0000000000000000000000000000000000000000
--- a/libs/WindowManager/Jetpack/androidx.window.extensions.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
diff --git a/libs/WindowManager/Jetpack/androidx.window.sidecar.xml b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f88a5f4ae039490556f0c12dbe05a7444452506c
--- /dev/null
+++ b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
similarity index 79%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java
rename to libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
index 7a3fbf3ad9b8e5491e88f00241e56f68ce82d86d..92e575804bbe3c901a50ea0697ece333634419a9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package androidx.window.extensions;
+package androidx.window.sidecar;
import static android.view.Display.DEFAULT_DISPLAY;
-import static androidx.window.extensions.ExtensionHelper.getWindowDisplay;
-import static androidx.window.extensions.ExtensionHelper.isInMultiWindow;
-import static androidx.window.extensions.ExtensionHelper.rotateRectToDisplayRotation;
-import static androidx.window.extensions.ExtensionHelper.transformToWindowSpaceRect;
+import static androidx.window.sidecar.SidecarHelper.getWindowDisplay;
+import static androidx.window.sidecar.SidecarHelper.isInMultiWindow;
+import static androidx.window.sidecar.SidecarHelper.rotateRectToDisplayRotation;
+import static androidx.window.sidecar.SidecarHelper.transformToWindowSpaceRect;
import android.content.ContentResolver;
import android.content.Context;
@@ -42,8 +42,8 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-class SettingsExtensionImpl extends StubExtension {
- private static final String TAG = "SettingsExtension";
+class SettingsSidecarImpl extends StubSidecar {
+ private static final String TAG = "SettingsSidecar";
private static final String DEVICE_POSTURE = "device_posture";
private static final String DISPLAY_FEATURES = "display_features";
@@ -106,7 +106,7 @@ class SettingsExtensionImpl extends StubExtension {
}
}
- SettingsExtensionImpl(Context context) {
+ SettingsSidecarImpl(Context context) {
mContext = context;
mSettingsObserver = new SettingsObserver();
}
@@ -118,29 +118,33 @@ class SettingsExtensionImpl extends StubExtension {
/** Update display features with values read from settings. */
private void updateDisplayFeatures() {
for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
- ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
+ SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
updateWindowLayout(windowToken, newLayout);
}
}
@NonNull
@Override
- public ExtensionDeviceState getDeviceState() {
+ public SidecarDeviceState getDeviceState() {
ContentResolver resolver = mContext.getContentResolver();
int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE,
- ExtensionDeviceState.POSTURE_UNKNOWN);
- return new ExtensionDeviceState(posture);
+ SidecarDeviceState.POSTURE_UNKNOWN);
+ SidecarDeviceState deviceState = new SidecarDeviceState();
+ deviceState.posture = posture;
+ return deviceState;
}
@NonNull
@Override
- public ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
- List displayFeatures = readDisplayFeatures(windowToken);
- return new ExtensionWindowLayoutInfo(displayFeatures);
+ public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
+ List displayFeatures = readDisplayFeatures(windowToken);
+ SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
+ windowLayoutInfo.displayFeatures = displayFeatures;
+ return windowLayoutInfo;
}
- private List readDisplayFeatures(IBinder windowToken) {
- List features = new ArrayList();
+ private List readDisplayFeatures(IBinder windowToken) {
+ List features = new ArrayList();
int displayId = getWindowDisplay(windowToken);
if (displayId != DEFAULT_DISPLAY) {
Log.w(TAG, "This sample doesn't support display features on secondary displays");
@@ -170,10 +174,10 @@ class SettingsExtensionImpl extends StubExtension {
int type;
switch (featureType) {
case FEATURE_TYPE_FOLD:
- type = ExtensionDisplayFeature.TYPE_FOLD;
+ type = SidecarDisplayFeature.TYPE_FOLD;
break;
case FEATURE_TYPE_HINGE:
- type = ExtensionDisplayFeature.TYPE_HINGE;
+ type = SidecarDisplayFeature.TYPE_HINGE;
break;
default: {
Log.e(TAG, "Malformed feature type: " + featureType);
@@ -189,8 +193,9 @@ class SettingsExtensionImpl extends StubExtension {
rotateRectToDisplayRotation(featureRect, displayId);
transformToWindowSpaceRect(featureRect, windowToken);
if (!featureRect.isEmpty()) {
- ExtensionDisplayFeature feature =
- new ExtensionDisplayFeature(featureRect, type);
+ SidecarDisplayFeature feature = new SidecarDisplayFeature();
+ feature.setRect(featureRect);
+ feature.setType(type);
features.add(feature);
} else {
Log.w(TAG, "Failed to adjust feature to window");
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
similarity index 92%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java
rename to libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
index c61f1ed2d179d7fc2a676cdf91a2d0e132e52c75..e5b6cff17b26521891ace4c0122542ce92599b3e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions;
+package androidx.window.sidecar;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_0;
@@ -32,12 +32,7 @@ import android.view.Surface;
import androidx.annotation.Nullable;
-/**
- * Toolkit class for calculation of the display feature bounds within the window.
- * NOTE: This sample implementation only works for Activity windows, because there is no public APIs
- * to obtain layout params or bounds for arbitrary windows.
- */
-class ExtensionHelper {
+class SidecarHelper {
/**
* Rotate the input rectangle specified in default display orientation to the current display
* rotation.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
similarity index 70%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
rename to libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
index 47349f11fb938285d59d1f76e5084b49a3005a82..0b4915ed5dac3c1d9124d5fb1ce1e1e7917a3505 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions;
+package androidx.window.sidecar;
import android.content.Context;
@@ -22,14 +22,13 @@ import android.content.Context;
* Provider class that will instantiate the library implementation. It must be included in the
* vendor library, and the vendor implementation must match the signature of this class.
*/
-public class ExtensionProvider {
-
+public class SidecarProvider {
/**
- * The support library will instantiate the vendor implementation using this interface.
- * @return An implementation of {@link ExtensionInterface}.
+ * Provide a simple implementation of {@link SidecarInterface} that can be replaced by
+ * an OEM by overriding this method.
*/
- public static ExtensionInterface getExtensionImpl(Context context) {
- return new SettingsExtensionImpl(context);
+ public static SidecarInterface getSidecarImpl(Context context) {
+ return new SettingsSidecarImpl(context);
}
/**
@@ -37,6 +36,6 @@ public class ExtensionProvider {
* @return API version string in MAJOR.MINOR.PATCH-description format.
*/
public static String getApiVersion() {
- return "1.0.0-settings_sample";
+ return "0.1.0-settings_sample";
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
new file mode 100644
index 0000000000000000000000000000000000000000..199c37315c0745b670dd176a30b5d362c2eac612
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.sidecar;
+
+import android.os.IBinder;
+
+import androidx.annotation.NonNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Basic implementation of the {@link SidecarInterface}. An OEM can choose to use it as the base
+ * class for their implementation.
+ */
+abstract class StubSidecar implements SidecarInterface {
+
+ private SidecarCallback mSidecarCallback;
+ private final Set mWindowLayoutChangeListenerTokens = new HashSet<>();
+ private boolean mDeviceStateChangeListenerRegistered;
+
+ StubSidecar() {
+ }
+
+ @Override
+ public void setSidecarCallback(@NonNull SidecarCallback sidecarCallback) {
+ this.mSidecarCallback = sidecarCallback;
+ }
+
+ @Override
+ public void onWindowLayoutChangeListenerAdded(@NonNull IBinder iBinder) {
+ this.mWindowLayoutChangeListenerTokens.add(iBinder);
+ this.onListenersChanged();
+ }
+
+ @Override
+ public void onWindowLayoutChangeListenerRemoved(@NonNull IBinder iBinder) {
+ this.mWindowLayoutChangeListenerTokens.remove(iBinder);
+ this.onListenersChanged();
+ }
+
+ @Override
+ public void onDeviceStateListenersChanged(boolean isEmpty) {
+ this.mDeviceStateChangeListenerRegistered = !isEmpty;
+ this.onListenersChanged();
+ }
+
+ void updateDeviceState(SidecarDeviceState newState) {
+ if (this.mSidecarCallback != null) {
+ mSidecarCallback.onDeviceStateChanged(newState);
+ }
+ }
+
+ void updateWindowLayout(@NonNull IBinder windowToken,
+ @NonNull SidecarWindowLayoutInfo newLayout) {
+ if (this.mSidecarCallback != null) {
+ mSidecarCallback.onWindowLayoutChanged(windowToken, newLayout);
+ }
+ }
+
+ @NonNull
+ Set getWindowsListeningForLayoutChanges() {
+ return mWindowLayoutChangeListenerTokens;
+ }
+
+ protected boolean hasListeners() {
+ return !mWindowLayoutChangeListenerTokens.isEmpty() || mDeviceStateChangeListenerRegistered;
+ }
+
+ protected abstract void onListenersChanged();
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
deleted file mode 100644
index 0ebbb86daf82b67bbe92d56b096b2598a3b8d5b6..0000000000000000000000000000000000000000
Binary files a/libs/WindowManager/Jetpack/window-extensions-release.aar and /dev/null differ
diff --git a/libs/WindowManager/Jetpack/window-sidecar-release.aar b/libs/WindowManager/Jetpack/window-sidecar-release.aar
new file mode 100644
index 0000000000000000000000000000000000000000..50f101d7d1814c1273df92ef3165b5ee22182669
Binary files /dev/null and b/libs/WindowManager/Jetpack/window-sidecar-release.aar differ
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index eb6ee9525bb929e6989b33a94363b5bd410b5218..5f231ffe4786276122d14c818e88bb552b1dc832 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -157,7 +157,7 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
table_value->dataType = entry->type;
table_value->data = entry->value;
- return Result(ResTable_entry_handle::managed(table_entry));
+ return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); }));
}
static bool is_word_aligned(const void* data) {
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 2bfc7fc38d1c36cf90f2fd2bef99d6599d88456d..e351a46d633acedc6c5098f6fdbabffbf1419c9f 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -41,7 +41,7 @@
namespace android {
constexpr const static uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const static uint32_t kIdmapCurrentVersion = 0x00000003u;
+constexpr const static uint32_t kIdmapCurrentVersion = 0x00000004u;
/**
* In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
@@ -1601,8 +1601,8 @@ class ResTable_entry_handle {
entry_ = handle.entry_;
}
- inline static ResTable_entry_handle managed(ResTable_entry* entry) {
- return ResTable_entry_handle(std::shared_ptr(entry));
+ inline static ResTable_entry_handle managed(ResTable_entry* entry, void (*deleter)(void *)) {
+ return ResTable_entry_handle(std::shared_ptr(entry, deleter));
}
inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) {
@@ -1746,6 +1746,9 @@ struct Idmap_header {
uint32_t target_crc32;
uint32_t overlay_crc32;
+ uint32_t fulfilled_policies;
+ uint8_t enforce_overlayable;
+
uint8_t target_path[256];
uint8_t overlay_path[256];
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index 62e98662e68d2cd0f039092b3a8dec3099b1bfbb..f1ed59279fdb47bee0004f87f32601ef5c500b8c 100644
Binary files a/libs/androidfw/tests/data/overlay/overlay.apk and b/libs/androidfw/tests/data/overlay/overlay.apk differ
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 3759ed6500336b1927647a3cbb33c2bee6a3baa3..29c5eb6a9ccfd1d7b88ffe1a1de349e65f5966b5 100644
Binary files a/libs/androidfw/tests/data/overlay/overlay.idmap and b/libs/androidfw/tests/data/overlay/overlay.idmap differ
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index aa8849b642b14a3e07d2c881a8c572b28f7f89d2..8f67f97fb4bc00ea9a5f7aca27f234ef3a54ad53 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -48,29 +48,20 @@ static void setScissor(int viewportHeight, const SkIRect& clip) {
glScissor(clip.fLeft, y, clip.width(), height);
}
-static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) {
+static void GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) {
GrRenderTargetContext* renderTargetContext =
canvas->internal_private_accessTopLayerRenderTargetContext();
- if (!renderTargetContext) {
- ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
- return false;
- }
+ LOG_ALWAYS_FATAL_IF(!renderTargetContext, "Failed to retrieve GrRenderTargetContext");
GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget();
- if (!renderTarget) {
- ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
- return false;
- }
+ LOG_ALWAYS_FATAL_IF(!renderTarget, "accessRenderTarget failed");
GrGLFramebufferInfo fboInfo;
- if (!renderTarget->getBackendRenderTarget().getGLFramebufferInfo(&fboInfo)) {
- ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
- return false;
- }
+ LOG_ALWAYS_FATAL_IF(!renderTarget->getBackendRenderTarget().getGLFramebufferInfo(&fboInfo),
+ "getGLFrameBufferInfo failed");
*outFboID = fboInfo.fFBOID;
*outFboSize = SkISize::Make(renderTargetContext->width(), renderTargetContext->height());
- return true;
}
void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
@@ -85,11 +76,12 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
return;
}
+ // flush will create a GrRenderTarget if not already present.
+ canvas->flush();
+
GLuint fboID = 0;
SkISize fboSize;
- if (!GetFboDetails(canvas, &fboID, &fboSize)) {
- return;
- }
+ GetFboDetails(canvas, &fboID, &fboSize);
SkIRect surfaceBounds = canvas->internal_private_getTopLayerBounds();
SkIRect clipBounds = canvas->getDeviceClipBounds();
@@ -143,7 +135,6 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
// ensure that the framebuffer that the webview will render into is bound before we clear
// the stencil and/or draw the functor.
- canvas->flush();
glViewport(0, 0, info.width, info.height);
glBindFramebuffer(GL_FRAMEBUFFER, fboID);
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index d177855e5a7d830852e183d3c231ad0081ab89d4..1e5877356e8dd638c129fe885da992de8c45277c 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -42,7 +42,7 @@ namespace renderthread {
// to the screen resolution. This is meant to be a conservative default based on
// that analysis. The 4.0f is used because the default pixel format is assumed to
// be ARGB_8888.
-#define SURFACE_SIZE_MULTIPLIER (5.0f * 4.0f)
+#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
#define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
CacheManager::CacheManager()
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
index af641101141114399af47f9254a5672e16ec2b62..d291ec001daf03795beb60bb772699e0fc6f4366 100644
--- a/libs/incident/Android.bp
+++ b/libs/incident/Android.bp
@@ -95,7 +95,17 @@ cc_test {
name: "libincident_test",
test_config: "AndroidTest.xml",
defaults: ["libincidentpriv_defaults"],
- test_suites: ["device-tests"],
+ test_suites: ["device-tests", "mts"],
+ compile_multilib: "both",
+ multilib: {
+ lib64: {
+ suffix: "64",
+ },
+ lib32: {
+ suffix: "32",
+ },
+ },
+ require_root: true,
include_dirs: [
"frameworks/base/libs/incident/include",
diff --git a/libs/incident/AndroidTest.xml b/libs/incident/AndroidTest.xml
index 7c0b04471d1397f17cb496a9ffa6d1dde9b3743c..b6b3f85968263819d361801c1df47e580567798b 100644
--- a/libs/incident/AndroidTest.xml
+++ b/libs/incident/AndroidTest.xml
@@ -16,13 +16,17 @@
-
+
+
+
diff --git a/libs/incident/TEST_MAPPING b/libs/incident/TEST_MAPPING
index 59ebe7664b5f3703b4e7d5e76a9ac897e820beb9..25e000092ecf98e9d32aec095bb6023b6c298440 100644
--- a/libs/incident/TEST_MAPPING
+++ b/libs/incident/TEST_MAPPING
@@ -2,9 +2,6 @@
"presubmit": [
{
"name": "libincident_test"
- },
- {
- "name": "GtsLibIncidentTests"
}
]
}
diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java
index 36b86899f2d8fcfa126eae519b25f43b2304e7e0..f5595e89b80745307a503ce5c4d26aef07f3247b 100644
--- a/location/java/android/location/AbstractListenerManager.java
+++ b/location/java/android/location/AbstractListenerManager.java
@@ -29,6 +29,8 @@ import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -193,7 +195,7 @@ abstract class AbstractListenerManager {
protected abstract void unregisterService() throws RemoteException;
@Nullable
- protected TRequest merge(@NonNull TRequest[] requests) {
+ protected TRequest merge(@NonNull List requests) {
for (TRequest request : requests) {
Preconditions.checkArgument(request == null,
"merge() has to be overridden for non-null requests.");
@@ -221,9 +223,9 @@ abstract class AbstractListenerManager {
return mListeners.valueAt(0).getRequest();
}
- TRequest[] requests = (TRequest[]) new Object[mListeners.size()];
+ ArrayList requests = new ArrayList<>(mListeners.size());
for (int index = 0; index < mListeners.size(); index++) {
- requests[index] = mListeners.valueAt(index).getRequest();
+ requests.add(mListeners.valueAt(index).getRequest());
}
return merge(requests);
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index a112bdd0ce03c43c79ec8beeec6e3720d5943a89..241e9399be0a223aae063e49a4badfdb19e56000 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1951,7 +1951,8 @@ public class LocationManager {
}
try {
- return mGnssStatusListenerManager.addListener(listener, Runnable::run);
+ return mGnssStatusListenerManager.addListener(listener,
+ new HandlerExecutor(new Handler()));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2086,7 +2087,7 @@ public class LocationManager {
@Deprecated
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean addNmeaListener(@NonNull OnNmeaMessageListener listener) {
- return addNmeaListener(Runnable::run, listener);
+ return addNmeaListener(listener, null);
}
/**
@@ -2583,9 +2584,15 @@ public class LocationManager {
}
public void cancel() {
+ remove();
+ }
+
+ private Consumer remove() {
+ Consumer consumer;
ICancellationSignal cancellationSignal;
synchronized (this) {
mExecutor = null;
+ consumer = mConsumer;
mConsumer = null;
if (mAlarmManager != null) {
@@ -2605,6 +2612,8 @@ public class LocationManager {
// ignore
}
}
+
+ return consumer;
}
public void fail() {
@@ -2663,16 +2672,10 @@ public class LocationManager {
}
private void acceptResult(Location location) {
- Consumer consumer;
- synchronized (this) {
- if (mConsumer == null) {
- return;
- }
- consumer = mConsumer;
- cancel();
+ Consumer consumer = remove();
+ if (consumer != null) {
+ consumer.accept(location);
}
-
- consumer.accept(location);
}
}
@@ -3028,14 +3031,14 @@ public class LocationManager {
@Override
@Nullable
- protected GnssRequest merge(@NonNull GnssRequest[] requests) {
- Preconditions.checkArgument(requests.length > 0);
+ protected GnssRequest merge(@NonNull List requests) {
+ Preconditions.checkArgument(!requests.isEmpty());
for (GnssRequest request : requests) {
if (request.isFullTracking()) {
return request;
}
}
- return requests[0];
+ return requests.get(0);
}
private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub {
diff --git a/location/java/android/location/package.html b/location/java/android/location/package.html
index 2355e725b6c503cf123740086b8c811e66950e8a..20c5c54d6921225fac20ef1865f2fde614c854e6 100644
--- a/location/java/android/location/package.html
+++ b/location/java/android/location/package.html
@@ -6,7 +6,7 @@
This API is not the recommended method for accessing Android location.
The
-Google Location Services API,
+Google Location Services API,
part of Google Play services, is the preferred way to add location-awareness to
your app. It offers a simpler API, higher accuracy, low-power geofencing, and
more. If you are currently using the android.location API, you are strongly
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 9846436b3ac8f4a460253de272053d25d5df47f6..67a040dba3e7f1e57024a9f3ac46ae4234769273 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -18,7 +18,6 @@ package com.android.internal.location;
import android.app.Notification;
import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -402,13 +401,9 @@ public class GpsNetInitiatedHandler {
mNiNotificationBuilder.setDefaults(0);
}
- // if not to popup dialog immediately, pending intent will open the dialog
- Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent();
- PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
mNiNotificationBuilder.setTicker(getNotifTicker(notif, mContext))
.setContentTitle(title)
- .setContentText(message)
- .setContentIntent(pi);
+ .setContentText(message);
notificationManager.notifyAsUser(null, notif.notificationId, mNiNotificationBuilder.build(),
UserHandle.ALL);
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 323bba3fdd71da5d2f75a61e731c3d835d7762a0..6c401161062fdf61360a0b75a008b3ec9716801e 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -32,7 +32,7 @@ import android.util.TimeUtils;
import com.android.internal.app.IBatteryStats;
import com.android.internal.location.nano.GnssLogsProto.GnssLog;
import com.android.internal.location.nano.GnssLogsProto.PowerMetrics;
-import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.FrameworkStatsLog;
import java.util.ArrayList;
@@ -124,8 +124,6 @@ public class GnssMetrics {
private long mL5SvStatusReportsUsedInFix;
/** Stats manager service for reporting atoms */
private StatsManager mStatsManager;
- /** Pull atom callback, this is called when atom pull request occurs */
- private StatsPullAtomCallbackImpl mPullAtomCallback;
/* Statds Logging Variables Section End */
public GnssMetrics(Context context, IBatteryStats stats) {
@@ -467,8 +465,8 @@ public class GnssMetrics {
mConstellationTypes = new boolean[GnssStatus.CONSTELLATION_COUNT];
}
- /** Class for storing statistics */
- private class Statistics {
+ /** Thread-safe class for storing statistics */
+ private static class Statistics {
private int mCount;
private double mSum;
@@ -476,7 +474,7 @@ public class GnssMetrics {
private long mLongSum;
/** Resets statistics */
- public void reset() {
+ public synchronized void reset() {
mCount = 0;
mSum = 0.0;
mSumSquare = 0.0;
@@ -484,7 +482,7 @@ public class GnssMetrics {
}
/** Adds an item */
- public void addItem(double item) {
+ public synchronized void addItem(double item) {
mCount++;
mSum += item;
mSumSquare += item * item;
@@ -492,17 +490,17 @@ public class GnssMetrics {
}
/** Returns number of items added */
- public int getCount() {
+ public synchronized int getCount() {
return mCount;
}
/** Returns mean */
- public double getMean() {
+ public synchronized double getMean() {
return mSum / mCount;
}
/** Returns standard deviation */
- public double getStandardDeviation() {
+ public synchronized double getStandardDeviation() {
double m = mSum / mCount;
m = m * m;
double v = mSumSquare / mCount;
@@ -513,7 +511,7 @@ public class GnssMetrics {
}
/** Returns long sum */
- public long getLongSum() {
+ public synchronized long getLongSum() {
return mLongSum;
}
}
@@ -623,11 +621,11 @@ public class GnssMetrics {
}
private void registerGnssStats() {
- mPullAtomCallback = new StatsPullAtomCallbackImpl();
+ StatsPullAtomCallbackImpl pullAtomCallback = new StatsPullAtomCallbackImpl();
mStatsManager.setPullAtomCallback(
FrameworkStatsLog.GNSS_STATS,
null, // use default PullAtomMetadata values
- BackgroundThread.getExecutor(), mPullAtomCallback);
+ ConcurrentUtils.DIRECT_EXECUTOR, pullAtomCallback);
}
/**
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index fe0c2d21d442443e19887a296f353bd5c8116847..158482a6a8339ee7b51dfc810ba93d2a67ddf49b 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -452,6 +452,10 @@ public final class AudioAttributes implements Parcelable {
| FLAG_NO_SYSTEM_CAPTURE | FLAG_CAPTURE_PRIVATE;
private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED |
FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY;
+ /* mask of flags that can be set by SDK and System APIs through the Builder */
+ private static final int FLAG_ALL_API_SET = FLAG_ALL_PUBLIC
+ | FLAG_BYPASS_INTERRUPTION_POLICY
+ | FLAG_BYPASS_MUTE;
/**
* Indicates that the audio may be captured by any app.
@@ -838,7 +842,7 @@ public final class AudioAttributes implements Parcelable {
* @return the same Builder instance.
*/
public Builder setFlags(int flags) {
- flags &= AudioAttributes.FLAG_ALL_PUBLIC;
+ flags &= AudioAttributes.FLAG_ALL_API_SET;
mFlags |= flags;
return this;
}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 2cca669a592bec1f9baf091f2561c19004f88de0..00a4c7e19f34d6fb89be1510afd68dcd9115c0dd 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -448,8 +448,7 @@ public final class AudioDeviceInfo {
* Returns an array of supported encapsulation modes for the device.
*
* The array can include any of the {@code AudioTrack} encapsulation modes,
- * e.g. {@link AudioTrack#ENCAPSULATION_MODE_NONE},
- * or {@link AudioTrack#ENCAPSULATION_MODE_ELEMENTARY_STREAM}.
+ * e.g. {@link AudioTrack#ENCAPSULATION_MODE_ELEMENTARY_STREAM}.
*
* @return An array of supported encapsulation modes for the device. This
* may be an empty array if no encapsulation modes are supported.
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index ea7a556f835b13e4dca5de203dadfc321259db15..3ac71b2cff1d157a85f1cbd558f333201d803560 100755
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4597,6 +4597,12 @@ public class AudioManager {
}
}
+ /**
+ * @hide
+ * Volume behavior for an audio device that has no particular volume behavior set. Invalid as
+ * an argument to {@link #setDeviceVolumeBehavior(int, String, int)}.
+ */
+ public static final int DEVICE_VOLUME_BEHAVIOR_UNSET = -1;
/**
* @hide
* Volume behavior for an audio device where a software attenuation is applied
@@ -4647,6 +4653,18 @@ public class AudioManager {
@Retention(RetentionPolicy.SOURCE)
public @interface DeviceVolumeBehavior {}
+ /** @hide */
+ @IntDef({
+ DEVICE_VOLUME_BEHAVIOR_UNSET,
+ DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ DEVICE_VOLUME_BEHAVIOR_FULL,
+ DEVICE_VOLUME_BEHAVIOR_FIXED,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceVolumeBehaviorState {}
+
/**
* @hide
* Throws IAE on an invalid volume behavior value
@@ -4713,7 +4731,7 @@ public class AudioManager {
* @return the volume behavior for the device
*/
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public @DeviceVolumeBehavior int getDeviceVolumeBehavior(int deviceType,
+ public @DeviceVolumeBehaviorState int getDeviceVolumeBehavior(int deviceType,
@Nullable String deviceAddress) {
// verify arguments
AudioDeviceInfo.enforceValidAudioDeviceTypeOut(deviceType);
@@ -4728,8 +4746,8 @@ public class AudioManager {
* @return the volume behavior for the device
*/
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public @DeviceVolumeBehavior int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device)
- {
+ public @DeviceVolumeBehaviorState int getDeviceVolumeBehavior(
+ @NonNull AudioDeviceAttributes device) {
// verify arguments (validity of device type is enforced in server)
Objects.requireNonNull(device);
// communicate with service
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 9f3fc5d94a98988d8984590c3288afbba9973b24..1c0a526f536c8e4a5db3854b6853b115d46f7215 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -280,7 +280,7 @@ public class AudioTrack extends PlayerBase
/**
* Encapsulation metadata type for framework tuner information.
*
- * TODO(b/147778408) Link: Fill in Tuner API info.
+ * Refer to the Android Media TV Tuner API for details.
*/
public static final int ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER = 1;
diff --git a/media/java/android/media/IMediaRouter2.aidl b/media/java/android/media/IMediaRouter2.aidl
index a8b82ba401ebd67a683e13398ad7f5958837bd65..fe15f0e67b1dceaeb36c195af08963e792e5defb 100644
--- a/media/java/android/media/IMediaRouter2.aidl
+++ b/media/java/android/media/IMediaRouter2.aidl
@@ -24,6 +24,8 @@ import android.os.Bundle;
* @hide
*/
oneway interface IMediaRouter2 {
+ void notifyRouterRegistered(in List currentRoutes,
+ in RoutingSessionInfo currentSystemSessionInfo);
void notifyRoutesAdded(in List routes);
void notifyRoutesRemoved(in List routes);
void notifyRoutesChanged(in List routes);
@@ -32,7 +34,8 @@ oneway interface IMediaRouter2 {
void notifySessionReleased(in RoutingSessionInfo sessionInfo);
/**
* Gets hints of the new session for the given route.
- * Call MediaRouterService#notifySessionHintsForCreatingSession to pass the result.
+ * Call MediaRouterService#requestCreateSessionWithRouter2 to pass the result.
*/
- void getSessionHintsForCreatingSession(long uniqueRequestId, in MediaRoute2Info route);
+ void requestCreateSessionByManager(long uniqueRequestId, in RoutingSessionInfo oldSession,
+ in MediaRoute2Info route);
}
diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl
index 5925d380115c0d8989a1284fa378d8045c526222..5113dc2058e09633525b480cfe4c984ea42003de 100644
--- a/media/java/android/media/IMediaRouter2Manager.aidl
+++ b/media/java/android/media/IMediaRouter2Manager.aidl
@@ -24,8 +24,9 @@ import android.media.RoutingSessionInfo;
* {@hide}
*/
oneway interface IMediaRouter2Manager {
- void notifySessionCreated(int requestId, in RoutingSessionInfo sessionInfo);
- void notifySessionUpdated(in RoutingSessionInfo sessionInfo);
+ void notifySessionCreated(int requestId, in RoutingSessionInfo session);
+ void notifySessionUpdated(in RoutingSessionInfo session);
+ void notifySessionReleased(in RoutingSessionInfo session);
void notifyPreferredFeaturesChanged(String packageName, in List preferredFeatures);
void notifyRoutesAdded(in List routes);
void notifyRoutesRemoved(in List routes);
diff --git a/media/java/android/media/IMediaRouterClient.aidl b/media/java/android/media/IMediaRouterClient.aidl
index 240ae796f95748e27f57c40e520bada4029e04c6..53122bb990d64c4f8c62d8048ebbbfb36b76e31e 100644
--- a/media/java/android/media/IMediaRouterClient.aidl
+++ b/media/java/android/media/IMediaRouterClient.aidl
@@ -23,4 +23,5 @@ oneway interface IMediaRouterClient {
void onStateChanged();
void onRestoreRoute();
void onSelectedRouteChanged(String routeId);
+ void onGlobalA2dpChanged(boolean a2dpOn);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 52bac671cc6f7fecdd5b6be08044053cd8942542..068f9689d06f9ccb4dd3e053761005222ea5bf16 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -44,7 +44,7 @@ interface IMediaRouterService {
void requestSetVolume(IMediaRouterClient client, String routeId, int volume);
void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction);
- // Note: When changing this file, match the order of methods below with
+ // Note: When changing this file, match the order of methods below with
// MediaRouterService.java for readability.
// Methods for MediaRouter2
@@ -57,10 +57,9 @@ interface IMediaRouterService {
in RouteDiscoveryPreference preference);
void setRouteVolumeWithRouter2(IMediaRouter2 router, in MediaRoute2Info route, int volume);
- void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId,
- in MediaRoute2Info route, in @nullable Bundle sessionHints);
- void notifySessionHintsForCreatingSession(IMediaRouter2 router, long uniqueRequestId,
- in MediaRoute2Info route, in @nullable Bundle sessionHints);
+ void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, long managerRequestId,
+ in RoutingSessionInfo oldSession, in MediaRoute2Info route,
+ in @nullable Bundle sessionHints);
void selectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
void deselectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
void transferToRouteWithRouter2(IMediaRouter2 router, String sessionId,
@@ -76,7 +75,7 @@ interface IMediaRouterService {
in MediaRoute2Info route, int volume);
void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId,
- String packageName, in @nullable MediaRoute2Info route);
+ in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route);
void selectRouteWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, in MediaRoute2Info route);
void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId,
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index ba87f2bbffb57e3d27ae97628ec27ee460f48cb3..87c3bb9ffabe17d3dc9e871130f11f351395f8cc 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -200,6 +200,20 @@ public class ImageReader implements AutoCloseable {
*
* Using other combinations may result in {@link IllegalArgumentException}.
*
+ *
+ * If the {@link ImageReader} is used as an output target for a {@link
+ * android.hardware.camera2.CameraDevice}, and if the usage flag contains
+ * {@link HardwareBuffer#USAGE_VIDEO_ENCODE}, the timestamps of the
+ * {@link Image images} produced by the {@link ImageReader} won't be in the same timebase as
+ * {@link android.os.SystemClock#elapsedRealtimeNanos}, even if
+ * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE} is
+ * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME}.
+ * Instead, the timestamps will be roughly in the same timebase as in
+ * {@link android.os.SystemClock#uptimeMillis}, so that A/V synchronization could work for
+ * video recording. In this case, the timestamps from the {@link ImageReader} with
+ * {@link HardwareBuffer#USAGE_VIDEO_ENCODE} usage flag may not be directly comparable with
+ * timestamps of other streams or capture result metadata.
+ *
* @param width The default width in pixels of the Images that this reader will produce.
* @param height The default height in pixels of the Images that this reader will produce.
* @param format The format of the Image that this reader will produce. This must be one of the
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index c652628eb4258f9eae23d46309122b3557180db9..590def4d4cedf8a8977d89fc0ff716c2c70cd4ec 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.app.ActivityManager;
import android.content.Context;
import android.hardware.cas.V1_0.HidlCasPluginDescriptor;
import android.hardware.cas.V1_0.ICas;
@@ -43,6 +44,8 @@ import android.os.RemoteException;
import android.util.Log;
import android.util.Singleton;
+import com.android.internal.util.FrameworkStatsLog;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -122,6 +125,7 @@ public final class MediaCas implements AutoCloseable {
private String mTvInputServiceSessionId;
private int mClientId;
private int mCasSystemId;
+ private int mUserId;
private TunerResourceManager mTunerResourceManager = null;
private final Map mSessionMap = new HashMap<>();
@@ -673,6 +677,8 @@ public final class MediaCas implements AutoCloseable {
*/
public MediaCas(int CA_system_id) throws UnsupportedCasException {
try {
+ mCasSystemId = CA_system_id;
+ mUserId = ActivityManager.getCurrentUser();
IMediaCasService service = getService();
android.hardware.cas.V1_2.IMediaCasService serviceV12 =
android.hardware.cas.V1_2.IMediaCasService.castFrom(service);
@@ -721,7 +727,6 @@ public final class MediaCas implements AutoCloseable {
this(casSystemId);
Objects.requireNonNull(context, "context must not be null");
- mCasSystemId = casSystemId;
mTunerResourceManager = (TunerResourceManager)
context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
if (mTunerResourceManager != null) {
@@ -925,10 +930,18 @@ public final class MediaCas implements AutoCloseable {
mICas.openSession(cb);
MediaCasException.throwExceptionIfNeeded(cb.mStatus);
addSessionToResourceMap(cb.mSession, sessionResourceHandle);
+ Log.d(TAG, "Write Stats Log for succeed to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__SUCCEEDED);
return cb.mSession;
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
+ Log.d(TAG, "Write Stats Log for fail to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__FAILED);
return null;
}
@@ -964,10 +977,18 @@ public final class MediaCas implements AutoCloseable {
mICasV12.openSession_1_2(sessionUsage, scramblingMode, cb);
MediaCasException.throwExceptionIfNeeded(cb.mStatus);
addSessionToResourceMap(cb.mSession, sessionResourceHandle);
+ Log.d(TAG, "Write Stats Log for succeed to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__SUCCEEDED);
return cb.mSession;
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
+ Log.d(TAG, "Write Stats Log for fail to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__FAILED);
return null;
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 62d76c0d2d583cd603e35803d82924632fa50590..0780c6875eb5afa600f8af69391cbf38f9ca60cd 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2762,7 +2762,7 @@ final public class MediaCodec {
builder.append(hexdigits.charAt(key[i] & 0x0f));
}
builder.append("], iv [");
- for (int i = 0; i < key.length; i++) {
+ for (int i = 0; i < iv.length; i++) {
builder.append(hexdigits.charAt((iv[i] & 0xf0) >> 4));
builder.append(hexdigits.charAt(iv[i] & 0x0f));
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 197786f424902265708dceb3bd97ec387051b65c..c2168f12a3514ac9a11911e2ed6a2b26b3262374 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -654,6 +654,9 @@ public final class MediaCodecInfo {
*
*
* The following table summarizes the format keys considered by this method.
+ * This is especially important to consider when targeting a higher SDK version than the
+ * minimum SDK version, as this method will disregard some keys on devices below the target
+ * SDK version.
*
*
*
@@ -668,7 +671,7 @@ public final class MediaCodecInfo {
*
*
*
as above, plus
* {@link MediaFormat#KEY_FRAME_RATE}
*
- *
{@link android.os.Build.VERSION_CODES#M}
+ *
{@link android.os.Build.VERSION_CODES#M}
*
- *
{@link android.os.Build.VERSION_CODES#N}
- *
as above, plus
+ *
{@link android.os.Build.VERSION_CODES#N}
+ *
as above, plus
* {@link MediaFormat#KEY_PROFILE},
*
* {@link MediaFormat#KEY_BIT_RATE}
- *
as above, plus
+ *
as above, plus
* {@link MediaFormat#KEY_PROFILE},
* {@link MediaFormat#KEY_LEVEL}+,
*
* {@link MediaFormat#KEY_BIT_RATE},
* {@link CodecCapabilities#FEATURE_IntraRefresh}E
+ *
+ *
{@link android.os.Build.VERSION_CODES#N_MR1}
+ *
+ *
{@link android.os.Build.VERSION_CODES#O}
+ *
as above, plus
+ * {@link CodecCapabilities#FEATURE_PartialFrame}D
+ *
+ *
{@link android.os.Build.VERSION_CODES#O_MR1}
+ *
+ *
{@link android.os.Build.VERSION_CODES#P}
+ *
+ *
{@link android.os.Build.VERSION_CODES#Q}
+ *
as above, plus
+ * {@link CodecCapabilities#FEATURE_FrameParsing}D,
+ * {@link CodecCapabilities#FEATURE_MultipleFrames},
+ * {@link CodecCapabilities#FEATURE_DynamicTimestamp}
+ *
+ *
{@link android.os.Build.VERSION_CODES#R}
+ *
as above, plus
+ * {@link CodecCapabilities#FEATURE_LowLatency}D
*
*
*
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index e5ad569bb24f4c337a4f481367ef0a2387fe93cf..7e9d2d809fddfeced190cfa0571d61015c33db1d 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -226,7 +226,7 @@ public final class MediaRoute2Info implements Parcelable {
public static final int TYPE_GROUP = 2000;
/**
- * Media feature: Live audio.
+ * Route feature: Live audio.
*
* A route that supports live audio routing will allow the media audio stream
* to be sent to supported destinations. This can include internal speakers or
@@ -241,7 +241,7 @@ public final class MediaRoute2Info implements Parcelable {
public static final String FEATURE_LIVE_AUDIO = "android.media.route.feature.LIVE_AUDIO";
/**
- * Media feature: Live video.
+ * Route feature: Live video.
*
* A route that supports live video routing will allow a mirrored version
* of the device's primary display or a customized
@@ -262,7 +262,14 @@ public final class MediaRoute2Info implements Parcelable {
public static final String FEATURE_LIVE_VIDEO = "android.media.route.feature.LIVE_VIDEO";
/**
- * Media feature: Remote playback.
+ * Route feature: Local playback.
+ * @hide
+ */
+ public static final String FEATURE_LOCAL_PLAYBACK =
+ "android.media.route.feature.LOCAL_PLAYBACK";
+
+ /**
+ * Route feature: Remote playback.
*
* A route that supports remote playback routing will allow an application to send
* requests to play content remotely to supported destinations.
@@ -283,7 +290,7 @@ public final class MediaRoute2Info implements Parcelable {
"android.media.route.feature.REMOTE_PLAYBACK";
/**
- * Media feature: Remote audio playback.
+ * Route feature: Remote audio playback.
*
* A route that supports remote audio playback routing will allow an application to send
* requests to play audio content remotely to supported destinations.
@@ -295,7 +302,7 @@ public final class MediaRoute2Info implements Parcelable {
"android.media.route.feature.REMOTE_AUDIO_PLAYBACK";
/**
- * Media feature: Remote video playback.
+ * Route feature: Remote video playback.
*
* A route that supports remote video playback routing will allow an application to send
* requests to play video content remotely to supported destinations.
@@ -306,6 +313,14 @@ public final class MediaRoute2Info implements Parcelable {
public static final String FEATURE_REMOTE_VIDEO_PLAYBACK =
"android.media.route.feature.REMOTE_VIDEO_PLAYBACK";
+ /**
+ * Route feature: Remote group playback.
+ *
+ * @hide
+ */
+ public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
+ "android.media.route.feature.REMOTE_GROUP_PLAYBACK";
+
final String mId;
final CharSequence mName;
final List mFeatures;
@@ -317,9 +332,10 @@ public final class MediaRoute2Info implements Parcelable {
@ConnectionState
final int mConnectionState;
final String mClientPackageName;
- final int mVolume;
- final int mVolumeMax;
final int mVolumeHandling;
+ final int mVolumeMax;
+ final int mVolume;
+ final String mAddress;
final Bundle mExtras;
final String mProviderId;
@@ -336,6 +352,7 @@ public final class MediaRoute2Info implements Parcelable {
mVolumeHandling = builder.mVolumeHandling;
mVolumeMax = builder.mVolumeMax;
mVolume = builder.mVolume;
+ mAddress = builder.mAddress;
mExtras = builder.mExtras;
mProviderId = builder.mProviderId;
}
@@ -353,6 +370,7 @@ public final class MediaRoute2Info implements Parcelable {
mVolumeHandling = in.readInt();
mVolumeMax = in.readInt();
mVolume = in.readInt();
+ mAddress = in.readString();
mExtras = in.readBundle();
mProviderId = in.readString();
}
@@ -483,6 +501,15 @@ public final class MediaRoute2Info implements Parcelable {
return mVolume;
}
+ /**
+ * Gets the hardware address of the route if available.
+ * @hide
+ */
+ @Nullable
+ public String getAddress() {
+ return mAddress;
+ }
+
@Nullable
public Bundle getExtras() {
return mExtras == null ? null : new Bundle(mExtras);
@@ -564,6 +591,7 @@ public final class MediaRoute2Info implements Parcelable {
&& (mVolumeHandling == other.mVolumeHandling)
&& (mVolumeMax == other.mVolumeMax)
&& (mVolume == other.mVolume)
+ && Objects.equals(mAddress, other.mAddress)
&& Objects.equals(mProviderId, other.mProviderId);
}
@@ -572,7 +600,7 @@ public final class MediaRoute2Info implements Parcelable {
// Note: mExtras is not included.
return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
- mProviderId);
+ mAddress, mProviderId);
}
@Override
@@ -614,6 +642,7 @@ public final class MediaRoute2Info implements Parcelable {
dest.writeInt(mVolumeHandling);
dest.writeInt(mVolumeMax);
dest.writeInt(mVolume);
+ dest.writeString(mAddress);
dest.writeBundle(mExtras);
dest.writeString(mProviderId);
}
@@ -637,6 +666,7 @@ public final class MediaRoute2Info implements Parcelable {
int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
int mVolumeMax;
int mVolume;
+ String mAddress;
Bundle mExtras;
String mProviderId;
@@ -669,24 +699,7 @@ public final class MediaRoute2Info implements Parcelable {
* @param routeInfo the existing instance to copy data from.
*/
public Builder(@NonNull MediaRoute2Info routeInfo) {
- Objects.requireNonNull(routeInfo, "routeInfo must not be null");
-
- mId = routeInfo.mId;
- mName = routeInfo.mName;
- mFeatures = new ArrayList<>(routeInfo.mFeatures);
- mType = routeInfo.mType;
- mIsSystem = routeInfo.mIsSystem;
- mIconUri = routeInfo.mIconUri;
- mDescription = routeInfo.mDescription;
- mConnectionState = routeInfo.mConnectionState;
- mClientPackageName = routeInfo.mClientPackageName;
- mVolumeHandling = routeInfo.mVolumeHandling;
- mVolumeMax = routeInfo.mVolumeMax;
- mVolume = routeInfo.mVolume;
- if (routeInfo.mExtras != null) {
- mExtras = new Bundle(routeInfo.mExtras);
- }
- mProviderId = routeInfo.mProviderId;
+ this(routeInfo.mId, routeInfo);
}
/**
@@ -715,6 +728,7 @@ public final class MediaRoute2Info implements Parcelable {
mVolumeHandling = routeInfo.mVolumeHandling;
mVolumeMax = routeInfo.mVolumeMax;
mVolume = routeInfo.mVolume;
+ mAddress = routeInfo.mAddress;
if (routeInfo.mExtras != null) {
mExtras = new Bundle(routeInfo.mExtras);
}
@@ -864,6 +878,16 @@ public final class MediaRoute2Info implements Parcelable {
return this;
}
+ /**
+ * Sets the hardware address of the route.
+ * @hide
+ */
+ @NonNull
+ public Builder setAddress(String address) {
+ mAddress = address;
+ return this;
+ }
+
/**
* Sets a bundle of extras for the route.
*
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 72162c44ec29282cae80d6682d0cd02c23b9647f..93fe06ac99aa7112fcf1dec5e37a9c6d2c09d5ca 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -40,8 +40,10 @@ import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Deque;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -69,6 +71,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public abstract class MediaRoute2ProviderService extends Service {
private static final String TAG = "MR2ProviderService";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
/**
* The {@link Intent} action that must be declared as handled by the service.
@@ -132,15 +135,21 @@ public abstract class MediaRoute2ProviderService extends Service {
@Retention(RetentionPolicy.SOURCE)
public @interface Reason {}
+ private static final int MAX_REQUEST_IDS_SIZE = 500;
+
private final Handler mHandler;
private final Object mSessionLock = new Object();
+ private final Object mRequestIdsLock = new Object();
private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false);
private MediaRoute2ProviderServiceStub mStub;
private IMediaRoute2ProviderServiceCallback mRemoteCallback;
- private MediaRoute2ProviderInfo mProviderInfo;
+ private volatile MediaRoute2ProviderInfo mProviderInfo;
+
+ @GuardedBy("mRequestIdsLock")
+ private final Deque mRequestIds = new ArrayDeque<>(MAX_REQUEST_IDS_SIZE);
@GuardedBy("mSessionLock")
- private ArrayMap mSessionInfo = new ArrayMap<>();
+ private final ArrayMap mSessionInfo = new ArrayMap<>();
public MediaRoute2ProviderService() {
mHandler = new Handler(Looper.getMainLooper());
@@ -167,8 +176,8 @@ public abstract class MediaRoute2ProviderService extends Service {
/**
* Called when a volume setting is requested on a route of the provider
*
- * @param requestId the id of this request
- * @param routeId the id of the route
+ * @param requestId the ID of this request
+ * @param routeId the ID of the route
* @param volume the target volume
* @see MediaRoute2Info.Builder#setVolume(int)
*/
@@ -178,8 +187,8 @@ public abstract class MediaRoute2ProviderService extends Service {
* Called when {@link MediaRouter2.RoutingController#setVolume(int)} is called on
* a routing session of the provider
*
- * @param requestId the id of this request
- * @param sessionId the id of the routing session
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the routing session
* @param volume the target volume
* @see RoutingSessionInfo.Builder#setVolume(int)
*/
@@ -188,7 +197,7 @@ public abstract class MediaRoute2ProviderService extends Service {
/**
* Gets information of the session with the given id.
*
- * @param sessionId id of the session
+ * @param sessionId the ID of the session
* @return information of the session with the given id.
* null if the session is released or ID is not valid.
*/
@@ -218,7 +227,7 @@ public abstract class MediaRoute2ProviderService extends Service {
* If this session is created without any creation request, use {@link #REQUEST_ID_NONE}
* as the request ID.
*
- * @param requestId id of the previous request to create this session provided in
+ * @param requestId the ID of the previous request to create this session provided in
* {@link #onCreateSession(long, String, String, Bundle)}. Can be
* {@link #REQUEST_ID_NONE} if this session is created without any request.
* @param sessionInfo information of the new session.
@@ -230,26 +239,32 @@ public abstract class MediaRoute2ProviderService extends Service {
@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+ if (DEBUG) {
+ Log.d(TAG, "notifySessionCreated: Creating a session. requestId=" + requestId
+ + ", sessionInfo=" + sessionInfo);
+ }
+
+ if (requestId != REQUEST_ID_NONE && !removeRequestId(requestId)) {
+ Log.w(TAG, "notifySessionCreated: The requestId doesn't exist. requestId=" + requestId);
+ return;
+ }
+
String sessionId = sessionInfo.getId();
synchronized (mSessionLock) {
if (mSessionInfo.containsKey(sessionId)) {
- // TODO: Notify failure to the requester, and throw exception if needed.
- Log.w(TAG, "Ignoring duplicate session id.");
+ Log.w(TAG, "notifySessionCreated: Ignoring duplicate session id.");
return;
}
mSessionInfo.put(sessionInfo.getId(), sessionInfo);
- }
- if (mRemoteCallback == null) {
- return;
- }
- try {
- // TODO: Calling binder calls in multiple thread may cause timing issue.
- // Consider to change implementations to avoid the problems.
- // For example, post binder calls, always send all sessions at once, etc.
- mRemoteCallback.notifySessionCreated(requestId, sessionInfo);
- } catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session created.");
+ if (mRemoteCallback == null) {
+ return;
+ }
+ try {
+ mRemoteCallback.notifySessionCreated(requestId, sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session created.");
+ }
}
}
@@ -260,53 +275,61 @@ public abstract class MediaRoute2ProviderService extends Service {
public final void notifySessionUpdated(@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+ if (DEBUG) {
+ Log.d(TAG, "notifySessionUpdated: Updating session id=" + sessionInfo);
+ }
+
String sessionId = sessionInfo.getId();
synchronized (mSessionLock) {
if (mSessionInfo.containsKey(sessionId)) {
mSessionInfo.put(sessionId, sessionInfo);
} else {
- Log.w(TAG, "Ignoring unknown session info.");
+ Log.w(TAG, "notifySessionUpdated: Ignoring unknown session info.");
return;
}
- }
- if (mRemoteCallback == null) {
- return;
- }
- try {
- mRemoteCallback.notifySessionUpdated(sessionInfo);
- } catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session info changed.");
+ if (mRemoteCallback == null) {
+ return;
+ }
+ try {
+ mRemoteCallback.notifySessionUpdated(sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session info changed.");
+ }
}
}
/**
* Notifies that the session is released.
*
- * @param sessionId id of the released session.
+ * @param sessionId the ID of the released session.
* @see #onReleaseSession(long, String)
*/
public final void notifySessionReleased(@NonNull String sessionId) {
if (TextUtils.isEmpty(sessionId)) {
throw new IllegalArgumentException("sessionId must not be empty");
}
+ if (DEBUG) {
+ Log.d(TAG, "notifySessionReleased: Releasing session id=" + sessionId);
+ }
+
RoutingSessionInfo sessionInfo;
synchronized (mSessionLock) {
sessionInfo = mSessionInfo.remove(sessionId);
- }
- if (sessionInfo == null) {
- Log.w(TAG, "Ignoring unknown session info.");
- return;
- }
+ if (sessionInfo == null) {
+ Log.w(TAG, "notifySessionReleased: Ignoring unknown session info.");
+ return;
+ }
- if (mRemoteCallback == null) {
- return;
- }
- try {
- mRemoteCallback.notifySessionReleased(sessionInfo);
- } catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session info changed.");
+ if (mRemoteCallback == null) {
+ return;
+ }
+ try {
+ mRemoteCallback.notifySessionReleased(sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session released.", ex);
+ }
}
}
@@ -326,6 +349,13 @@ public abstract class MediaRoute2ProviderService extends Service {
if (mRemoteCallback == null) {
return;
}
+
+ if (!removeRequestId(requestId)) {
+ Log.w(TAG, "notifyRequestFailed: The requestId doesn't exist. requestId="
+ + requestId);
+ return;
+ }
+
try {
mRemoteCallback.notifyRequestFailed(requestId, reason);
} catch (RemoteException ex) {
@@ -349,9 +379,9 @@ public abstract class MediaRoute2ProviderService extends Service {
* If you can't create the session or want to reject the request, call
* {@link #notifyRequestFailed(long, int)} with the given {@code requestId}.
*
- * @param requestId the id of this request
+ * @param requestId the ID of this request
* @param packageName the package name of the application that selected the route
- * @param routeId the id of the route initially being connected
+ * @param routeId the ID of the route initially being connected
* @param sessionHints an optional bundle of app-specific arguments sent by
* {@link MediaRouter2}, or null if none. The contents of this bundle
* may affect the result of session creation.
@@ -373,8 +403,8 @@ public abstract class MediaRoute2ProviderService extends Service {
* Note: Calling {@link #notifySessionReleased(String)} will NOT trigger
* this method to be called.
*
- * @param requestId the id of this request
- * @param sessionId id of the session being released.
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session being released.
* @see #notifySessionReleased(String)
* @see #getSessionInfo(String)
*/
@@ -385,9 +415,9 @@ public abstract class MediaRoute2ProviderService extends Service {
* After the route is selected, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
- * @param requestId the id of this request
- * @param sessionId id of the session
- * @param routeId id of the route
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session
+ * @param routeId the ID of the route
*/
public abstract void onSelectRoute(long requestId, @NonNull String sessionId,
@NonNull String routeId);
@@ -397,9 +427,9 @@ public abstract class MediaRoute2ProviderService extends Service {
* After the route is deselected, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
- * @param requestId the id of this request
- * @param sessionId id of the session
- * @param routeId id of the route
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session
+ * @param routeId the ID of the route
*/
public abstract void onDeselectRoute(long requestId, @NonNull String sessionId,
@NonNull String routeId);
@@ -409,9 +439,9 @@ public abstract class MediaRoute2ProviderService extends Service {
* After the transfer is finished, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
- * @param requestId the id of this request
- * @param sessionId id of the session
- * @param routeId id of the route
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session
+ * @param routeId the ID of the route
*/
public abstract void onTransferToRoute(long requestId, @NonNull String sessionId,
@NonNull String routeId);
@@ -469,20 +499,76 @@ public abstract class MediaRoute2ProviderService extends Service {
try {
mRemoteCallback.updateState(mProviderInfo);
} catch (RemoteException ex) {
- Log.w(TAG, "Failed to send onProviderInfoUpdated");
+ Log.w(TAG, "Failed to publish provider state.", ex);
+ }
+ }
+
+ /**
+ * Adds a requestId in the request ID list whose max size is {@link #MAX_REQUEST_IDS_SIZE}.
+ * When the max size is reached, the first element is removed (FIFO).
+ */
+ private void addRequestId(long requestId) {
+ synchronized (mRequestIdsLock) {
+ if (mRequestIds.size() >= MAX_REQUEST_IDS_SIZE) {
+ mRequestIds.removeFirst();
+ }
+ mRequestIds.addLast(requestId);
+ }
+ }
+
+ /**
+ * Removes the given {@code requestId} from received request ID list.
+ *
+ * Returns whether the list contains the {@code requestId}. These are the cases when the list
+ * doesn't contain the given {@code requestId}:
+ *
+ *
This service has never received a request with the requestId.
+ *
{@link #notifyRequestFailed} or {@link #notifySessionCreated} already has been called
+ * for the requestId.
+ *
+ */
+ private boolean removeRequestId(long requestId) {
+ synchronized (mRequestIdsLock) {
+ return mRequestIds.removeFirstOccurrence(requestId);
}
}
final class MediaRoute2ProviderServiceStub extends IMediaRoute2ProviderService.Stub {
MediaRoute2ProviderServiceStub() { }
- boolean checkCallerisSystem() {
+ private boolean checkCallerIsSystem() {
return Binder.getCallingUid() == Process.SYSTEM_UID;
}
+ private boolean checkSessionIdIsValid(String sessionId, String description) {
+ if (TextUtils.isEmpty(sessionId)) {
+ Log.w(TAG, description + ": Ignoring empty sessionId from system service.");
+ return false;
+ }
+ if (getSessionInfo(sessionId) == null) {
+ Log.w(TAG, description + ": Ignoring unknown session from system service. "
+ + "sessionId=" + sessionId);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkRouteIdIsValid(String routeId, String description) {
+ if (TextUtils.isEmpty(routeId)) {
+ Log.w(TAG, description + ": Ignoring empty routeId from system service.");
+ return false;
+ }
+ if (mProviderInfo == null || mProviderInfo.getRoute(routeId) == null) {
+ Log.w(TAG, description + ": Ignoring unknown route from system service. "
+ + "routeId=" + routeId);
+ return false;
+ }
+ return true;
+ }
+
@Override
public void setCallback(IMediaRoute2ProviderServiceCallback callback) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::setCallback,
@@ -491,7 +577,7 @@ public abstract class MediaRoute2ProviderService extends Service {
@Override
public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(
@@ -501,9 +587,13 @@ public abstract class MediaRoute2ProviderService extends Service {
@Override
public void setRouteVolume(long requestId, String routeId, int volume) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
+ if (!checkRouteIdIsValid(routeId, "setRouteVolume")) {
+ return;
+ }
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetRouteVolume,
MediaRoute2ProviderService.this, requestId, routeId, volume));
}
@@ -511,72 +601,82 @@ public abstract class MediaRoute2ProviderService extends Service {
@Override
public void requestCreateSession(long requestId, String packageName, String routeId,
@Nullable Bundle requestCreateSession) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
+ return;
+ }
+ if (!checkRouteIdIsValid(routeId, "requestCreateSession")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
MediaRoute2ProviderService.this, requestId, packageName, routeId,
requestCreateSession));
}
- //TODO: Ignore requests with unknown session ID.
@Override
public void selectRoute(long requestId, String sessionId, String routeId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "selectRoute: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "selectRoute")
+ || !checkRouteIdIsValid(routeId, "selectRoute")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@Override
public void deselectRoute(long requestId, String sessionId, String routeId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "deselectRoute: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "deselectRoute")
+ || !checkRouteIdIsValid(routeId, "deselectRoute")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onDeselectRoute,
MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@Override
public void transferToRoute(long requestId, String sessionId, String routeId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "transferToRoute: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "transferToRoute")
+ || !checkRouteIdIsValid(routeId, "transferToRoute")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferToRoute,
MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@Override
public void setSessionVolume(long requestId, String sessionId, int volume) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
+ return;
+ }
+ if (!checkSessionIdIsValid(sessionId, "setSessionVolume")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetSessionVolume,
MediaRoute2ProviderService.this, requestId, sessionId, volume));
}
@Override
public void releaseSession(long requestId, String sessionId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "releaseSession: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
MediaRoute2ProviderService.this, requestId, sessionId));
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 12fc3a60cf2831a6dca28068c5555441e2312e67..6fa378724240eda7474cc304b8c56ff49e26da67 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -221,12 +221,11 @@ public class MediaRouter {
if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
forceUseDefaultRoute = false;
- mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
- if (mCurAudioRoutesInfo.bluetoothName != null) {
+ if (newRoutes.bluetoothName != null) {
if (mBluetoothA2dpRoute == null) {
// BT connected
final RouteInfo info = new RouteInfo(mSystemCategory);
- info.mName = mCurAudioRoutesInfo.bluetoothName;
+ info.mName = newRoutes.bluetoothName;
info.mDescription = mResources.getText(
com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
@@ -234,13 +233,14 @@ public class MediaRouter {
mBluetoothA2dpRoute = info;
addRouteStatic(mBluetoothA2dpRoute);
} else {
- mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.bluetoothName;
+ mBluetoothA2dpRoute.mName = newRoutes.bluetoothName;
dispatchRouteChanged(mBluetoothA2dpRoute);
}
} else if (mBluetoothA2dpRoute != null) {
// BT disconnected
- removeRouteStatic(mBluetoothA2dpRoute);
+ RouteInfo btRoute = mBluetoothA2dpRoute;
mBluetoothA2dpRoute = null;
+ removeRouteStatic(btRoute);
}
audioRoutesChanged = true;
}
@@ -256,6 +256,7 @@ public class MediaRouter {
}
}
}
+ mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
}
boolean isBluetoothA2dpOn() {
@@ -621,33 +622,27 @@ public class MediaRouter {
final class Client extends IMediaRouterClient.Stub {
@Override
public void onStateChanged() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (Client.this == mClient) {
- updateClientState();
- }
+ mHandler.post(() -> {
+ if (Client.this == mClient) {
+ updateClientState();
}
});
}
@Override
public void onRestoreRoute() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- // Skip restoring route if the selected route is not a system audio route,
- // MediaRouter is initializing, or mClient was changed.
- if (Client.this != mClient || mSelectedRoute == null
- || (mSelectedRoute != mDefaultAudioVideo
- && mSelectedRoute != mBluetoothA2dpRoute)) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
- }
- mSelectedRoute.select();
+ mHandler.post(() -> {
+ // Skip restoring route if the selected route is not a system audio route,
+ // MediaRouter is initializing, or mClient was changed.
+ if (Client.this != mClient || mSelectedRoute == null
+ || (mSelectedRoute != mDefaultAudioVideo
+ && mSelectedRoute != mBluetoothA2dpRoute)) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
}
+ mSelectedRoute.select();
});
}
@@ -659,6 +654,19 @@ public class MediaRouter {
}
});
}
+
+ // Called when the selection of a connected device (phone speaker or BT devices)
+ // is changed.
+ @Override
+ public void onGlobalA2dpChanged(boolean a2dpOn) {
+ mHandler.post(() -> {
+ if (mSelectedRoute == mDefaultAudioVideo && a2dpOn) {
+ setSelectedRoute(mBluetoothA2dpRoute, false);
+ } else if (mSelectedRoute == mBluetoothA2dpRoute && !a2dpOn) {
+ setSelectedRoute(mDefaultAudioVideo, false);
+ }
+ });
+ }
}
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 6179b483ca5347bccffa370cbfc3b95b08fbb1c8..ff52a1abc6f49613751d9bcf7b7762bf98d7dae1 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -36,7 +36,6 @@ import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -54,7 +53,7 @@ import java.util.stream.Collectors;
* Media Router 2 allows applications to control the routing of media channels
* and streams from the current device to remote speakers and devices.
*/
-// TODO: Add method names at the beginning of log messages. (e.g. updateControllerOnHandler)
+// TODO(b/157873330): Add method names at the beginning of log messages. (e.g. selectRoute)
// Not only MediaRouter2, but also to service / manager / provider.
// TODO: ensure thread-safe and document it
public final class MediaRouter2 {
@@ -62,6 +61,11 @@ public final class MediaRouter2 {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final Object sRouterLock = new Object();
+ // The maximum time for the old routing controller available after transfer.
+ private static final int TRANSFER_TIMEOUT_MS = 30_000;
+ // The manager request ID representing that no manager is involved.
+ private static final long MANAGER_REQUEST_ID_NONE = MediaRoute2ProviderService.REQUEST_ID_NONE;
+
@GuardedBy("sRouterLock")
private static MediaRouter2 sInstance;
@@ -80,7 +84,7 @@ public final class MediaRouter2 {
private final String mPackageName;
@GuardedBy("sRouterLock")
- final Map mRoutes = new HashMap<>();
+ final Map mRoutes = new ArrayMap<>();
final RoutingController mSystemController;
@@ -92,9 +96,9 @@ public final class MediaRouter2 {
MediaRouter2Stub mStub;
@GuardedBy("sRouterLock")
- private final Map mRoutingControllers = new ArrayMap<>();
+ private final Map mNonSystemRoutingControllers = new ArrayMap<>();
- private final AtomicInteger mControllerCreationRequestCnt = new AtomicInteger(1);
+ private final AtomicInteger mNextRequestId = new AtomicInteger(1);
final Handler mHandler;
@GuardedBy("sRouterLock")
@@ -151,7 +155,7 @@ public final class MediaRouter2 {
*
* @hide
*/
- public static boolean checkRouteListContainsRouteId(@NonNull List routeList,
+ static boolean checkRouteListContainsRouteId(@NonNull List routeList,
@NonNull String routeId) {
for (MediaRoute2Info info : routeList) {
if (TextUtils.equals(routeId, info.getId())) {
@@ -196,7 +200,7 @@ public final class MediaRouter2 {
try {
mMediaRouterService.setDiscoveryRequestWithRouter2(mStub, mDiscoveryPreference);
} catch (RemoteException ex) {
- Log.e(TAG, "registerRouteCallback: Unable to set discovery request.");
+ Log.e(TAG, "registerRouteCallback: Unable to set discovery request.", ex);
}
}
}
@@ -214,7 +218,7 @@ public final class MediaRouter2 {
if (!mRouteCallbackRecords.remove(
new RouteCallbackRecord(null, routeCallback, null))) {
- Log.w(TAG, "Ignoring unknown callback");
+ Log.w(TAG, "unregisterRouteCallback: Ignoring unknown callback");
return;
}
@@ -227,10 +231,10 @@ public final class MediaRouter2 {
mMediaRouterService.setDiscoveryRequestWithRouter2(
mStub, mDiscoveryPreference);
} catch (RemoteException ex) {
- Log.e(TAG, "unregisterRouteCallback: Unable to set discovery request.");
+ Log.e(TAG, "unregisterRouteCallback: Unable to set discovery request.", ex);
}
}
- if (mRouteCallbackRecords.size() == 0) {
+ if (mRouteCallbackRecords.isEmpty() && mNonSystemRoutingControllers.isEmpty()) {
try {
mMediaRouterService.unregisterRouter2(mStub);
} catch (RemoteException ex) {
@@ -258,8 +262,6 @@ public final class MediaRouter2 {
* Gets the unmodifiable list of {@link MediaRoute2Info routes} currently
* known to the media router.
*
- * {@link MediaRoute2Info#isSystemRoute() System routes} such as phone speaker,
- * Bluetooth devices are always included in the list.
* Please note that the list can be changed before callbacks are invoked.
*
*
@@ -274,8 +276,7 @@ public final class MediaRouter2 {
List filteredRoutes = new ArrayList<>();
for (MediaRoute2Info route : mRoutes.values()) {
- if (route.isSystemRoute()
- || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
filteredRoutes.add(route);
}
}
@@ -378,6 +379,7 @@ public final class MediaRouter2 {
*/
public void transferTo(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
+ Log.v(TAG, "Transferring to route: " + route);
transfer(getCurrentController(), route);
}
@@ -399,19 +401,31 @@ public final class MediaRouter2 {
Objects.requireNonNull(controller, "controller must not be null");
Objects.requireNonNull(route, "route must not be null");
- // TODO: Check thread-safety
- if (!mRoutes.containsKey(route.getId())) {
+ boolean routeFound;
+ synchronized (sRouterLock) {
+ // TODO: Check thread-safety
+ routeFound = mRoutes.containsKey(route.getId());
+ }
+ if (!routeFound) {
notifyTransferFailure(route);
return;
}
+
if (controller.getRoutingSessionInfo().getTransferableRoutes().contains(route.getId())) {
controller.transferToRoute(route);
return;
}
- final int requestId = mControllerCreationRequestCnt.getAndIncrement();
+ requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE);
+ }
+
+ void requestCreateController(@NonNull RoutingController controller,
+ @NonNull MediaRoute2Info route, long managerRequestId) {
- ControllerCreationRequest request = new ControllerCreationRequest(requestId, route);
+ final int requestId = mNextRequestId.getAndIncrement();
+
+ ControllerCreationRequest request = new ControllerCreationRequest(requestId,
+ managerRequestId, route, controller);
mControllerCreationRequests.add(request);
OnGetControllerHintsListener listener = mOnGetControllerHintsListener;
@@ -430,11 +444,15 @@ public final class MediaRouter2 {
if (stub != null) {
try {
mMediaRouterService.requestCreateSessionWithRouter2(
- stub, requestId, route, controllerHints);
+ stub, requestId, managerRequestId,
+ controller.getRoutingSessionInfo(), route, controllerHints);
} catch (RemoteException ex) {
- Log.e(TAG, "transfer: Unable to request to create controller.", ex);
- mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
- MediaRouter2.this, requestId, null));
+ Log.e(TAG, "createControllerForTransfer: "
+ + "Failed to request for creating a controller.", ex);
+ mControllerCreationRequests.remove(request);
+ if (managerRequestId == MANAGER_REQUEST_ID_NONE) {
+ notifyTransferFailure(route);
+ }
}
}
}
@@ -460,7 +478,8 @@ public final class MediaRouter2 {
}
/**
- * Gets the list of currently non-released {@link RoutingController routing controllers}.
+ * Gets the list of currently active {@link RoutingController routing controllers} on which
+ * media can be played.
*
* Note: The list returned here will never be empty. The first element in the list is
* always the {@link #getSystemController() system controller}.
@@ -470,7 +489,7 @@ public final class MediaRouter2 {
List result = new ArrayList<>();
result.add(0, mSystemController);
synchronized (sRouterLock) {
- result.addAll(mRoutingControllers.values());
+ result.addAll(mNonSystemRoutingControllers.values());
}
return result;
}
@@ -495,28 +514,91 @@ public final class MediaRouter2 {
try {
mMediaRouterService.setRouteVolumeWithRouter2(stub, route, volume);
} catch (RemoteException ex) {
- Log.e(TAG, "Unable to send control request.", ex);
+ Log.e(TAG, "Unable to set route volume.", ex);
}
}
}
+ void syncRoutesOnHandler(List currentRoutes,
+ RoutingSessionInfo currentSystemSessionInfo) {
+ if (currentRoutes == null || currentRoutes.isEmpty() || currentSystemSessionInfo == null) {
+ Log.e(TAG, "syncRoutesOnHandler: Received wrong data. currentRoutes=" + currentRoutes
+ + ", currentSystemSessionInfo=" + currentSystemSessionInfo);
+ return;
+ }
+
+ List addedRoutes = new ArrayList<>();
+ List removedRoutes = new ArrayList<>();
+ List changedRoutes = new ArrayList<>();
+
+ synchronized (sRouterLock) {
+ List currentRoutesIds = currentRoutes.stream().map(MediaRoute2Info::getId)
+ .collect(Collectors.toList());
+
+ for (String routeId : mRoutes.keySet()) {
+ if (!currentRoutesIds.contains(routeId)) {
+ // This route is removed while the callback is unregistered.
+ MediaRoute2Info route = mRoutes.get(routeId);
+ if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ removedRoutes.add(mRoutes.get(routeId));
+ }
+ }
+ }
+
+ for (MediaRoute2Info route : currentRoutes) {
+ if (mRoutes.containsKey(route.getId())) {
+ if (!route.equals(mRoutes.get(route.getId()))) {
+ // This route is changed while the callback is unregistered.
+ if (route.hasAnyFeatures(
+ mDiscoveryPreference.getPreferredFeatures())) {
+ changedRoutes.add(route);
+ }
+ }
+ } else {
+ // This route is added while the callback is unregistered.
+ if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ addedRoutes.add(route);
+ }
+ }
+ }
+
+ mRoutes.clear();
+ for (MediaRoute2Info route : currentRoutes) {
+ mRoutes.put(route.getId(), route);
+ }
+
+ mShouldUpdateRoutes = true;
+ }
+
+ if (!addedRoutes.isEmpty()) {
+ notifyRoutesAdded(addedRoutes);
+ }
+ if (!removedRoutes.isEmpty()) {
+ notifyRoutesRemoved(removedRoutes);
+ }
+ if (!changedRoutes.isEmpty()) {
+ notifyRoutesChanged(changedRoutes);
+ }
+
+ RoutingSessionInfo oldInfo = mSystemController.getRoutingSessionInfo();
+ mSystemController.setRoutingSessionInfo(currentSystemSessionInfo);
+ if (!oldInfo.equals(currentSystemSessionInfo)) {
+ notifyControllerUpdated(mSystemController);
+ }
+ }
+
void addRoutesOnHandler(List routes) {
- // TODO: When onRoutesAdded is first called,
- // 1) clear mRoutes before adding the routes
- // 2) Call onRouteSelected(system_route, reason_fallback) if previously selected route
- // does not exist anymore. => We may need 'boolean MediaRoute2Info#isSystemRoute()'.
List addedRoutes = new ArrayList<>();
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
- if (route.isSystemRoute()
- || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
addedRoutes.add(route);
}
}
mShouldUpdateRoutes = true;
}
- if (addedRoutes.size() > 0) {
+ if (!addedRoutes.isEmpty()) {
notifyRoutesAdded(addedRoutes);
}
}
@@ -526,14 +608,13 @@ public final class MediaRouter2 {
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
mRoutes.remove(route.getId());
- if (route.isSystemRoute()
- || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
removedRoutes.add(route);
}
}
mShouldUpdateRoutes = true;
}
- if (removedRoutes.size() > 0) {
+ if (!removedRoutes.isEmpty()) {
notifyRoutesRemoved(removedRoutes);
}
}
@@ -543,13 +624,13 @@ public final class MediaRouter2 {
synchronized (sRouterLock) {
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
- if (route.isSystemRoute()
- || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
changedRoutes.add(route);
}
}
+ mShouldUpdateRoutes = true;
}
- if (changedRoutes.size() > 0) {
+ if (!changedRoutes.isEmpty()) {
notifyRoutesChanged(changedRoutes);
}
}
@@ -570,44 +651,40 @@ public final class MediaRouter2 {
}
}
- if (matchingRequest != null) {
- mControllerCreationRequests.remove(matchingRequest);
-
- MediaRoute2Info requestedRoute = matchingRequest.mRoute;
-
- if (sessionInfo == null) {
- // TODO: We may need to distinguish between failure and rejection.
- // One way can be introducing 'reason'.
- notifyTransferFailure(requestedRoute);
- return;
- } else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
- Log.w(TAG, "The session does not contain the requested route. "
- + "(requestedRouteId=" + requestedRoute.getId()
- + ", actualRoutes=" + sessionInfo.getSelectedRoutes()
- + ")");
- notifyTransferFailure(requestedRoute);
- return;
- } else if (!TextUtils.equals(requestedRoute.getProviderId(),
- sessionInfo.getProviderId())) {
- Log.w(TAG, "The session's provider ID does not match the requested route's. "
- + "(requested route's providerId=" + requestedRoute.getProviderId()
- + ", actual providerId=" + sessionInfo.getProviderId()
- + ")");
- notifyTransferFailure(requestedRoute);
- return;
- }
+ if (matchingRequest == null) {
+ Log.w(TAG, "createControllerOnHandler: Ignoring an unknown request.");
+ return;
}
+ mControllerCreationRequests.remove(matchingRequest);
+ MediaRoute2Info requestedRoute = matchingRequest.mRoute;
+
+ // TODO: Notify the reason for failure.
if (sessionInfo == null) {
+ notifyTransferFailure(requestedRoute);
+ return;
+ } else if (!TextUtils.equals(requestedRoute.getProviderId(),
+ sessionInfo.getProviderId())) {
+ Log.w(TAG, "The session's provider ID does not match the requested route's. "
+ + "(requested route's providerId=" + requestedRoute.getProviderId()
+ + ", actual providerId=" + sessionInfo.getProviderId()
+ + ")");
+ notifyTransferFailure(requestedRoute);
return;
}
- RoutingController oldController = getCurrentController();
- if (!oldController.releaseInternal(
- /* shouldReleaseSession= */ matchingRequest != null,
- /* shouldNotifyStop= */ false)) {
- // Could not release the controller since it was just released by other thread.
- oldController = getSystemController();
+ RoutingController oldController = matchingRequest.mOldController;
+ // When the old controller is released before transferred, treat it as a failure.
+ // This could also happen when transfer is requested twice or more.
+ if (!oldController.scheduleRelease()) {
+ Log.w(TAG, "createControllerOnHandler: "
+ + "Ignoring controller creation for released old controller. "
+ + "oldController=" + oldController);
+ if (!sessionInfo.isSystemSession()) {
+ new RoutingController(sessionInfo).release();
+ }
+ notifyTransferFailure(requestedRoute);
+ return;
}
RoutingController newController;
@@ -617,16 +694,11 @@ public final class MediaRouter2 {
} else {
newController = new RoutingController(sessionInfo);
synchronized (sRouterLock) {
- mRoutingControllers.put(newController.getId(), newController);
+ mNonSystemRoutingControllers.put(newController.getId(), newController);
}
}
- // Two controller can be same if stop() is called before the result of Cast -> Phone comes.
- if (oldController != newController) {
- notifyTransfer(oldController, newController);
- } else if (matchingRequest != null) {
- notifyTransferFailure(matchingRequest.mRoute);
- }
+ notifyTransfer(oldController, newController);
}
void updateControllerOnHandler(RoutingSessionInfo sessionInfo) {
@@ -645,7 +717,7 @@ public final class MediaRouter2 {
RoutingController matchingController;
synchronized (sRouterLock) {
- matchingController = mRoutingControllers.get(sessionInfo.getId());
+ matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId());
}
if (matchingController == null) {
@@ -671,10 +743,9 @@ public final class MediaRouter2 {
return;
}
- final String uniqueSessionId = sessionInfo.getId();
RoutingController matchingController;
synchronized (sRouterLock) {
- matchingController = mRoutingControllers.get(uniqueSessionId);
+ matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId());
}
if (matchingController == null) {
@@ -692,40 +763,29 @@ public final class MediaRouter2 {
return;
}
- matchingController.releaseInternal(
- /* shouldReleaseSession= */ false, /* shouldNotifyStop= */ true);
+ matchingController.releaseInternal(/* shouldReleaseSession= */ false);
}
- void onGetControllerHintsForCreatingSessionOnHandler(long uniqueRequestId,
- MediaRoute2Info route) {
- OnGetControllerHintsListener listener = mOnGetControllerHintsListener;
- Bundle controllerHints = null;
- if (listener != null) {
- controllerHints = listener.onGetControllerHints(route);
- if (controllerHints != null) {
- controllerHints = new Bundle(controllerHints);
+ void onRequestCreateControllerByManagerOnHandler(RoutingSessionInfo oldSession,
+ MediaRoute2Info route, long managerRequestId) {
+ RoutingController controller;
+ if (oldSession.isSystemSession()) {
+ controller = getSystemController();
+ } else {
+ synchronized (sRouterLock) {
+ controller = mNonSystemRoutingControllers.get(oldSession.getId());
}
}
-
- MediaRouter2Stub stub;
- synchronized (sRouterLock) {
- stub = mStub;
- }
- if (stub != null) {
- try {
- mMediaRouterService.notifySessionHintsForCreatingSession(
- stub, uniqueRequestId, route, controllerHints);
- } catch (RemoteException ex) {
- Log.e(TAG, "getSessionHintsOnHandler: Unable to request.", ex);
- }
+ if (controller == null) {
+ return;
}
+ requestCreateController(controller, route, managerRequestId);
}
private List filterRoutes(List routes,
RouteDiscoveryPreference discoveryRequest) {
return routes.stream()
- .filter(route -> route.isSystemRoute()
- || route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
+ .filter(route -> route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
.collect(Collectors.toList());
}
@@ -821,8 +881,13 @@ public final class MediaRouter2 {
/**
* Called when a media is transferred between two different routing controllers.
* This can happen by calling {@link #transferTo(MediaRoute2Info)}.
- * The {@code oldController} is released before this method is called, except for the
- * {@link #getSystemController() system controller}.
+ *
Override this to start playback with {@code newController}. You may want to get
+ * the status of the media that is being played with {@code oldController} and resume it
+ * continuously with {@code newController}.
+ * After this is called, any callbacks with {@code oldController} will not be invoked
+ * unless {@code oldController} is the {@link #getSystemController() system controller}.
+ * You need to {@link RoutingController#release() release} {@code oldController} before
+ * playing the media with {@code newController}.
*
* @param oldController the previous controller that controlled routing
* @param newController the new controller to control routing
@@ -841,16 +906,15 @@ public final class MediaRouter2 {
/**
* Called when a media routing stops. It can be stopped by a user or a provider.
* App should not continue playing media locally when this method is called.
- * The {@code oldController} is released before this method is called, except for the
- * {@link #getSystemController() system controller}.
+ * The {@code controller} is released before this method is called.
*
- * @param controller the controller that controlled the stopped media routing.
+ * @param controller the controller that controlled the stopped media routing
*/
public void onStop(@NonNull RoutingController controller) { }
}
/**
- * A listener interface to send an optional app-specific hints when creating the
+ * A listener interface to send optional app-specific hints when creating a
* {@link RoutingController}.
*/
public interface OnGetControllerHintsListener {
@@ -864,9 +928,9 @@ public final class MediaRouter2 {
* The method will be called on the same thread that calls
* {@link #transferTo(MediaRoute2Info)} or the main thread if it is requested by the system.
*
- * @param route The route to create controller with
+ * @param route the route to create a controller with
* @return An optional bundle of app-specific arguments to send to the provider,
- * or null if none. The contents of this bundle may affect the result of
+ * or {@code null} if none. The contents of this bundle may affect the result of
* controller creation.
* @see MediaRoute2ProviderService#onCreateSession(long, String, String, Bundle)
*/
@@ -879,10 +943,11 @@ public final class MediaRouter2 {
*/
public abstract static class ControllerCallback {
/**
- * Called when a controller is updated. (e.g., the selected routes of the
- * controller is changed or the volume of the controller is changed.)
+ * Called when a controller is updated. (e.g., when the selected routes of the
+ * controller is changed or when the volume of the controller is changed.)
*
- * @param controller the updated controller. Can be the system controller.
+ * @param controller the updated controller. It may be the
+ * {@link #getSystemController() system controller}.
* @see #getSystemController()
*/
public void onControllerUpdated(@NonNull RoutingController controller) { }
@@ -890,20 +955,28 @@ public final class MediaRouter2 {
/**
* A class to control media routing session in media route provider.
- * For example, selecting/deselecting/transferring routes to a session can be done through this
- * class. Instances are created by {@link #transferTo(MediaRoute2Info)}.
+ * For example, selecting/deselecting/transferring to routes of a session can be done through
+ * this. Instances are created when
+ * {@link TransferCallback#onTransfer(RoutingController, RoutingController)} is called,
+ * which is invoked after {@link #transferTo(MediaRoute2Info)} is called.
*/
public class RoutingController {
private final Object mControllerLock = new Object();
+ private static final int CONTROLLER_STATE_UNKNOWN = 0;
+ private static final int CONTROLLER_STATE_ACTIVE = 1;
+ private static final int CONTROLLER_STATE_RELEASING = 2;
+ private static final int CONTROLLER_STATE_RELEASED = 3;
+
@GuardedBy("mControllerLock")
private RoutingSessionInfo mSessionInfo;
@GuardedBy("mControllerLock")
- private volatile boolean mIsReleased;
+ private int mState;
RoutingController(@NonNull RoutingSessionInfo sessionInfo) {
mSessionInfo = sessionInfo;
+ mState = CONTROLLER_STATE_ACTIVE;
}
/**
@@ -917,7 +990,7 @@ public final class MediaRouter2 {
}
/**
- * Gets the original session id set by
+ * Gets the original session ID set by
* {@link RoutingSessionInfo.Builder#Builder(String, String)}.
*
* @hide
@@ -931,7 +1004,8 @@ public final class MediaRouter2 {
}
/**
- * @return the control hints used to control routing session if available.
+ * Gets the control hints used to control routing session if available.
+ * It is set by the media route provider.
*/
@Nullable
public Bundle getControlHints() {
@@ -977,7 +1051,9 @@ public final class MediaRouter2 {
}
/**
- * Gets information about how volume is handled on the session.
+ * Gets the information about how volume is handled on the session.
+ *
Please note that you may not control the volume of the session even when
+ * you can control the volume of each selected route in the session.
*
* @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
* {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}
@@ -1002,8 +1078,8 @@ public final class MediaRouter2 {
* Gets the current volume of the session.
*
* When it's available, it represents the volume of routing session, which is a group
- * of selected routes. To get the volume of a route,
- * use {@link MediaRoute2Info#getVolume()}.
+ * of selected routes. Use {@link MediaRoute2Info#getVolume()}
+ * to get the volume of a route,
*
* @see MediaRoute2Info#getVolume()
*/
@@ -1022,7 +1098,7 @@ public final class MediaRouter2 {
*/
public boolean isReleased() {
synchronized (mControllerLock) {
- return mIsReleased;
+ return mState == CONTROLLER_STATE_RELEASED;
}
}
@@ -1034,8 +1110,8 @@ public final class MediaRouter2 {
*
* The given route must satisfy all of the following conditions:
*
- *
ID should not be included in {@link #getSelectedRoutes()}
- *
ID should be included in {@link #getSelectableRoutes()}
+ *
It should not be included in {@link #getSelectedRoutes()}
+ *
It should be included in {@link #getSelectableRoutes()}
*
* If the route doesn't meet any of above conditions, it will be ignored.
*
@@ -1046,11 +1122,9 @@ public final class MediaRouter2 {
*/
public void selectRoute(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
- synchronized (mControllerLock) {
- if (mIsReleased) {
- Log.w(TAG, "selectRoute() called on released controller. Ignoring.");
- return;
- }
+ if (isReleased()) {
+ Log.w(TAG, "selectRoute: Called on released controller. Ignoring.");
+ return;
}
List selectedRoutes = getSelectedRoutes();
@@ -1080,12 +1154,12 @@ public final class MediaRouter2 {
/**
* Deselects a route from the remote session. After a route is deselected, the media is
- * expected to be stopped on the deselected routes.
+ * expected to be stopped on the deselected route.
*
* The given route must satisfy all of the following conditions:
*
- *
ID should be included in {@link #getSelectedRoutes()}
- *
ID should be included in {@link #getDeselectableRoutes()}
+ *
It should be included in {@link #getSelectedRoutes()}
+ *
It should be included in {@link #getDeselectableRoutes()}
*
* If the route doesn't meet any of above conditions, it will be ignored.
*
@@ -1095,11 +1169,9 @@ public final class MediaRouter2 {
*/
public void deselectRoute(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
- synchronized (mControllerLock) {
- if (mIsReleased) {
- Log.w(TAG, "deselectRoute() called on released controller. Ignoring.");
- return;
- }
+ if (isReleased()) {
+ Log.w(TAG, "deselectRoute: called on released controller. Ignoring.");
+ return;
}
List selectedRoutes = getSelectedRoutes();
@@ -1128,13 +1200,8 @@ public final class MediaRouter2 {
}
/**
- * Transfers to a given route for the remote session. The given route must satisfy
- * all of the following conditions:
- *
- *
ID should not be included in {@link RoutingSessionInfo#getSelectedRoutes()}
- *
ID should be included in {@link RoutingSessionInfo#getTransferableRoutes()}
- *
- * If the route doesn't meet any of above conditions, it will be ignored.
+ * Transfers to a given route for the remote session. The given route must be included
+ * in {@link RoutingSessionInfo#getTransferableRoutes()}.
*
* @see RoutingSessionInfo#getSelectedRoutes()
* @see RoutingSessionInfo#getTransferableRoutes()
@@ -1143,19 +1210,13 @@ public final class MediaRouter2 {
void transferToRoute(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
synchronized (mControllerLock) {
- if (mIsReleased) {
- Log.w(TAG, "transferToRoute() called on released controller. Ignoring.");
- return;
- }
-
- if (mSessionInfo.getSelectedRoutes().contains(route.getId())) {
- Log.w(TAG, "Ignoring transferring to a route that is already added. "
- + "route=" + route);
+ if (isReleased()) {
+ Log.w(TAG, "transferToRoute: Called on released controller. Ignoring.");
return;
}
if (!mSessionInfo.getTransferableRoutes().contains(route.getId())) {
- Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
+ Log.w(TAG, "Ignoring transferring to a non-transferable route=" + route);
return;
}
}
@@ -1182,19 +1243,17 @@ public final class MediaRouter2 {
*/
public void setVolume(int volume) {
if (getVolumeHandling() == MediaRoute2Info.PLAYBACK_VOLUME_FIXED) {
- Log.w(TAG, "setVolume: the routing session has fixed volume. Ignoring.");
+ Log.w(TAG, "setVolume: The routing session has fixed volume. Ignoring.");
return;
}
if (volume < 0 || volume > getVolumeMax()) {
- Log.w(TAG, "setVolume: the target volume is out of range. Ignoring");
+ Log.w(TAG, "setVolume: The target volume is out of range. Ignoring");
return;
}
- synchronized (mControllerLock) {
- if (mIsReleased) {
- Log.w(TAG, "setVolume is called on released controller. Ignoring.");
- return;
- }
+ if (isReleased()) {
+ Log.w(TAG, "setVolume: Called on released controller. Ignoring.");
+ return;
}
MediaRouter2Stub stub;
synchronized (sRouterLock) {
@@ -1210,47 +1269,82 @@ public final class MediaRouter2 {
}
/**
- * Release this controller and corresponding session.
+ * Releases this controller and the corresponding session.
* Any operations on this controller after calling this method will be ignored.
* The devices that are playing media will stop playing it.
*/
- // TODO: Add tests using {@link MediaRouter2Manager#getActiveSessions()}.
public void release() {
- releaseInternal(/* shouldReleaseSession= */ true, /* shouldNotifyStop= */ true);
+ releaseInternal(/* shouldReleaseSession= */ true);
}
/**
- * Returns {@code true} when succeeded to release, {@code false} if the controller is
- * already released.
+ * Schedules release of the controller.
+ * @return {@code true} if it's successfully scheduled, {@code false} if it's already
+ * scheduled to be released or released.
*/
- boolean releaseInternal(boolean shouldReleaseSession, boolean shouldNotifyStop) {
+ boolean scheduleRelease() {
synchronized (mControllerLock) {
- if (mIsReleased) {
- Log.w(TAG, "releaseInternal() called on released controller. Ignoring.");
+ if (mState != CONTROLLER_STATE_ACTIVE) {
return false;
}
- mIsReleased = true;
+ mState = CONTROLLER_STATE_RELEASING;
}
- MediaRouter2Stub stub;
synchronized (sRouterLock) {
- mRoutingControllers.remove(getId(), this);
- stub = mStub;
+ // It could happen if the controller is released by the another thread
+ // in between two locks
+ if (!mNonSystemRoutingControllers.remove(getId(), this)) {
+ // In that case, onStop isn't called so we return true to call onTransfer.
+ // It's also consistent with that the another thread acquires the lock later.
+ return true;
+ }
}
- if (shouldReleaseSession && stub != null) {
- try {
- mMediaRouterService.releaseSessionWithRouter2(stub, getId());
- } catch (RemoteException ex) {
- Log.e(TAG, "Unable to release session", ex);
+ mHandler.postDelayed(this::release, TRANSFER_TIMEOUT_MS);
+
+ return true;
+ }
+
+ void releaseInternal(boolean shouldReleaseSession) {
+ boolean shouldNotifyStop;
+
+ synchronized (mControllerLock) {
+ if (mState == CONTROLLER_STATE_RELEASED) {
+ if (DEBUG) {
+ Log.d(TAG, "releaseInternal: Called on released controller. Ignoring.");
+ }
+ return;
}
+ shouldNotifyStop = (mState == CONTROLLER_STATE_ACTIVE);
+ mState = CONTROLLER_STATE_RELEASED;
}
- if (shouldNotifyStop) {
- mHandler.sendMessage(obtainMessage(MediaRouter2::notifyStop, MediaRouter2.this,
- RoutingController.this));
+ synchronized (sRouterLock) {
+ mNonSystemRoutingControllers.remove(getId(), this);
+
+ if (shouldReleaseSession && mStub != null) {
+ try {
+ mMediaRouterService.releaseSessionWithRouter2(mStub, getId());
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to release session", ex);
+ }
+ }
+
+ if (shouldNotifyStop) {
+ mHandler.sendMessage(obtainMessage(MediaRouter2::notifyStop, MediaRouter2.this,
+ RoutingController.this));
+ }
+
+ if (mRouteCallbackRecords.isEmpty() && mNonSystemRoutingControllers.isEmpty()
+ && mStub != null) {
+ try {
+ mMediaRouterService.unregisterRouter2(mStub);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "releaseInternal: Unable to unregister media router.", ex);
+ }
+ mStub = null;
+ }
}
- return true;
}
@Override
@@ -1313,9 +1407,14 @@ public final class MediaRouter2 {
}
@Override
- boolean releaseInternal(boolean shouldReleaseSession, boolean shouldNotifyStop) {
+ boolean scheduleRelease() {
+ // SystemRoutingController can be always transferred
+ return true;
+ }
+
+ @Override
+ void releaseInternal(boolean shouldReleaseSession) {
// Do nothing. SystemRoutingController will never be released
- return false;
}
}
@@ -1366,8 +1465,7 @@ public final class MediaRouter2 {
if (!(obj instanceof TransferCallbackRecord)) {
return false;
}
- return mTransferCallback
- == ((TransferCallbackRecord) obj).mTransferCallback;
+ return mTransferCallback == ((TransferCallbackRecord) obj).mTransferCallback;
}
@Override
@@ -1405,15 +1503,28 @@ public final class MediaRouter2 {
static final class ControllerCreationRequest {
public final int mRequestId;
+ public final long mManagerRequestId;
public final MediaRoute2Info mRoute;
+ public final RoutingController mOldController;
- ControllerCreationRequest(int requestId, @NonNull MediaRoute2Info route) {
+ ControllerCreationRequest(int requestId, long managerRequestId,
+ @NonNull MediaRoute2Info route, @NonNull RoutingController oldController) {
mRequestId = requestId;
- mRoute = route;
+ mManagerRequestId = managerRequestId;
+ mRoute = Objects.requireNonNull(route, "route must not be null");
+ mOldController = Objects.requireNonNull(oldController,
+ "oldController must not be null");
}
}
class MediaRouter2Stub extends IMediaRouter2.Stub {
+ @Override
+ public void notifyRouterRegistered(List currentRoutes,
+ RoutingSessionInfo currentSystemSessionInfo) {
+ mHandler.sendMessage(obtainMessage(MediaRouter2::syncRoutesOnHandler,
+ MediaRouter2.this, currentRoutes, currentSystemSessionInfo));
+ }
+
@Override
public void notifyRoutesAdded(List routes) {
mHandler.sendMessage(obtainMessage(MediaRouter2::addRoutesOnHandler,
@@ -1451,11 +1562,11 @@ public final class MediaRouter2 {
}
@Override
- public void getSessionHintsForCreatingSession(long uniqueRequestId,
- @NonNull MediaRoute2Info route) {
+ public void requestCreateSessionByManager(long managerRequestId,
+ RoutingSessionInfo oldSession, MediaRoute2Info route) {
mHandler.sendMessage(obtainMessage(
- MediaRouter2::onGetControllerHintsForCreatingSessionOnHandler,
- MediaRouter2.this, uniqueRequestId, route));
+ MediaRouter2::onRequestCreateControllerByManagerOnHandler,
+ MediaRouter2.this, oldSession, route, managerRequestId));
}
}
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 4ebfce830a70d72bec26b047cc3e624e413bfac5..4b09a5f19fb03ca118836b700d15319cff9e6e2b 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -25,12 +25,14 @@ import android.content.Context;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.os.Handler;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
@@ -52,6 +54,15 @@ import java.util.stream.Collectors;
public final class MediaRouter2Manager {
private static final String TAG = "MR2Manager";
private static final Object sLock = new Object();
+ /**
+ * The request ID for requests not asked by this instance.
+ * Shouldn't be used for a valid request.
+ * @hide
+ */
+ public static final int REQUEST_ID_NONE = 0;
+ /** @hide */
+ @VisibleForTesting
+ public static final int TRANSFER_TIMEOUT_MS = 30_000;
@GuardedBy("sLock")
private static MediaRouter2Manager sInstance;
@@ -116,7 +127,7 @@ public final class MediaRouter2Manager {
CallbackRecord callbackRecord = new CallbackRecord(executor, callback);
if (!mCallbackRecords.addIfAbsent(callbackRecord)) {
- Log.w(TAG, "Ignoring to add the same callback twice.");
+ Log.w(TAG, "Ignoring to register the same callback twice.");
return;
}
}
@@ -151,8 +162,6 @@ public final class MediaRouter2Manager {
return null;
}
- //TODO: Use cache not to create array. For now, it's unclear when to purge the cache.
- //Do this when we finalize how to set control categories.
/**
* Gets available routes for an application.
*
@@ -162,20 +171,8 @@ public final class MediaRouter2Manager {
public List getAvailableRoutes(@NonNull String packageName) {
Objects.requireNonNull(packageName, "packageName must not be null");
- List routes = new ArrayList<>();
-
- List preferredFeatures = mPreferredFeaturesMap.get(packageName);
- if (preferredFeatures == null) {
- preferredFeatures = Collections.emptyList();
- }
- synchronized (mRoutesLock) {
- for (MediaRoute2Info route : mRoutes.values()) {
- if (route.isSystemRoute() || route.hasAnyFeatures(preferredFeatures)) {
- routes.add(route);
- }
- }
- }
- return routes;
+ List sessions = getRoutingSessions(packageName);
+ return getAvailableRoutesForRoutingSession(sessions.get(sessions.size() - 1));
}
/**
@@ -199,7 +196,7 @@ public final class MediaRouter2Manager {
}
synchronized (mRoutesLock) {
for (MediaRoute2Info route : mRoutes.values()) {
- if (route.isSystemRoute() || route.hasAnyFeatures(preferredFeatures)
+ if (route.hasAnyFeatures(preferredFeatures)
|| sessionInfo.getSelectedRoutes().contains(route.getId())
|| sessionInfo.getTransferableRoutes().contains(route.getId())) {
routes.add(route);
@@ -300,7 +297,7 @@ public final class MediaRouter2Manager {
}
/**
- * Gets the list of all discovered routes
+ * Gets the list of all discovered routes.
*/
@NonNull
public List getAllRoutes() {
@@ -318,6 +315,8 @@ public final class MediaRouter2Manager {
Objects.requireNonNull(packageName, "packageName must not be null");
Objects.requireNonNull(route, "route must not be null");
+ Log.v(TAG, "Selecting route. packageName= " + packageName + ", route=" + route);
+
List sessionInfos = getRoutingSessions(packageName);
RoutingSessionInfo targetSession = sessionInfos.get(sessionInfos.size() - 1);
transfer(targetSession, route);
@@ -339,30 +338,20 @@ public final class MediaRouter2Manager {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
Objects.requireNonNull(route, "route must not be null");
- //TODO: Ignore unknown route.
- if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
- transferToRoute(sessionInfo, route);
- return;
- }
+ Log.v(TAG, "Transferring routing session. session= " + sessionInfo + ", route=" + route);
- if (TextUtils.isEmpty(sessionInfo.getClientPackageName())) {
- Log.w(TAG, "transfer: Ignoring transfer without package name.");
- notifyTransferFailed(sessionInfo, route);
- return;
+ synchronized (mRoutesLock) {
+ if (!mRoutes.containsKey(route.getId())) {
+ Log.w(TAG, "transfer: Ignoring an unknown route id=" + route.getId());
+ notifyTransferFailed(sessionInfo, route);
+ return;
+ }
}
- Client client = getOrCreateClient();
- if (client != null) {
- try {
- int requestId = mNextRequestId.getAndIncrement();
- //TODO: Ensure that every request is eventually removed.
- mTransferRequests.add(new TransferRequest(requestId, sessionInfo, route));
-
- mMediaRouterService.requestCreateSessionWithManager(
- client, requestId, sessionInfo.getClientPackageName(), route);
- } catch (RemoteException ex) {
- Log.e(TAG, "Unable to select media route", ex);
- }
+ if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
+ transferToRoute(sessionInfo, route);
+ } else {
+ requestCreateSession(sessionInfo, route);
}
}
@@ -393,7 +382,7 @@ public final class MediaRouter2Manager {
int requestId = mNextRequestId.getAndIncrement();
mMediaRouterService.setRouteVolumeWithManager(client, requestId, route, volume);
} catch (RemoteException ex) {
- Log.e(TAG, "Unable to send control request.", ex);
+ Log.e(TAG, "Unable to set route volume.", ex);
}
}
}
@@ -423,7 +412,7 @@ public final class MediaRouter2Manager {
mMediaRouterService.setSessionVolumeWithManager(
client, requestId, sessionInfo.getId(), volume);
} catch (RemoteException ex) {
- Log.e(TAG, "Unable to send control request.", ex);
+ Log.e(TAG, "Unable to set session volume.", ex);
}
}
}
@@ -497,7 +486,6 @@ public final class MediaRouter2Manager {
notifyTransferFailed(matchingRequest.mOldSessionInfo, requestedRoute);
return;
}
- releaseSession(matchingRequest.mOldSessionInfo);
notifyTransferred(matchingRequest.mOldSessionInfo, sessionInfo);
}
@@ -518,15 +506,15 @@ public final class MediaRouter2Manager {
notifyRequestFailed(reason);
}
- void handleSessionsUpdated(RoutingSessionInfo sessionInfo) {
+ void handleSessionsUpdatedOnHandler(RoutingSessionInfo sessionInfo) {
for (TransferRequest request : mTransferRequests) {
String sessionId = request.mOldSessionInfo.getId();
if (!TextUtils.equals(sessionId, sessionInfo.getId())) {
continue;
}
if (sessionInfo.getSelectedRoutes().contains(request.mTargetRoute.getId())) {
- notifyTransferred(request.mOldSessionInfo, sessionInfo);
mTransferRequests.remove(request);
+ notifyTransferred(request.mOldSessionInfo, sessionInfo);
break;
}
}
@@ -560,6 +548,12 @@ public final class MediaRouter2Manager {
}
}
+ void notifySessionReleased(RoutingSessionInfo session) {
+ for (CallbackRecord record : mCallbackRecords) {
+ record.mExecutor.execute(() -> record.mCallback.onSessionReleased(session));
+ }
+ }
+
void notifyRequestFailed(int reason) {
for (CallbackRecord record : mCallbackRecords) {
record.mExecutor.execute(() -> record.mCallback.onRequestFailed(reason));
@@ -579,6 +573,10 @@ public final class MediaRouter2Manager {
}
void updatePreferredFeatures(String packageName, List preferredFeatures) {
+ if (preferredFeatures == null) {
+ mPreferredFeaturesMap.remove(packageName);
+ return;
+ }
List prevFeatures = mPreferredFeaturesMap.put(packageName, preferredFeatures);
if ((prevFeatures == null && preferredFeatures.size() == 0)
|| Objects.equals(preferredFeatures, prevFeatures)) {
@@ -597,7 +595,7 @@ public final class MediaRouter2Manager {
public List getSelectedRoutes(@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
- synchronized (sLock) {
+ synchronized (mRoutesLock) {
return sessionInfo.getSelectedRoutes().stream().map(mRoutes::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
@@ -613,7 +611,7 @@ public final class MediaRouter2Manager {
List selectedRouteIds = sessionInfo.getSelectedRoutes();
- synchronized (sLock) {
+ synchronized (mRoutesLock) {
return sessionInfo.getSelectableRoutes().stream()
.filter(routeId -> !selectedRouteIds.contains(routeId))
.map(mRoutes::get)
@@ -631,7 +629,7 @@ public final class MediaRouter2Manager {
List selectedRouteIds = sessionInfo.getSelectedRoutes();
- synchronized (sLock) {
+ synchronized (mRoutesLock) {
return sessionInfo.getDeselectableRoutes().stream()
.filter(routeId -> selectedRouteIds.contains(routeId))
.map(mRoutes::get)
@@ -727,65 +725,89 @@ public final class MediaRouter2Manager {
}
/**
- * Transfers to a given route for the remote session.
+ * Requests releasing a session.
+ *
+ * If a session is released, any operation on the session will be ignored.
+ * {@link Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)} with {@code null}
+ * session will be called when the session is released.
+ *
*
- * @hide
+ * @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)
*/
- void transferToRoute(@NonNull RoutingSessionInfo sessionInfo,
- @NonNull MediaRoute2Info route) {
+ public void releaseSession(@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
- Objects.requireNonNull(route, "route must not be null");
-
- if (sessionInfo.getSelectedRoutes().contains(route.getId())) {
- Log.w(TAG, "Ignoring transferring to a route that is already added. route="
- + route);
- return;
- }
- if (!sessionInfo.getTransferableRoutes().contains(route.getId())) {
- Log.w(TAG, "Ignoring transferring to a non-transferable route=" + route);
- return;
+ Client client = getOrCreateClient();
+ if (client != null) {
+ try {
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.releaseSessionWithManager(
+ client, requestId, sessionInfo.getId());
+ } catch (RemoteException ex) {
+ Log.e(TAG, "releaseSession: Failed to send a request", ex);
+ }
}
+ }
- int requestId = mNextRequestId.getAndIncrement();
- mTransferRequests.add(new TransferRequest(requestId, sessionInfo, route));
+ /**
+ * Transfers the remote session to the given route.
+ *
+ * @hide
+ */
+ private void transferToRoute(@NonNull RoutingSessionInfo session,
+ @NonNull MediaRoute2Info route) {
+ int requestId = createTransferRequest(session, route);
Client client = getOrCreateClient();
if (client != null) {
try {
mMediaRouterService.transferToRouteWithManager(
- client, requestId, sessionInfo.getId(), route);
+ client, requestId, session.getId(), route);
} catch (RemoteException ex) {
Log.e(TAG, "transferToRoute: Failed to send a request.", ex);
}
}
}
- /**
- * Requests releasing a session.
- *
- * If a session is released, any operation on the session will be ignored.
- * {@link Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)} with {@code null}
- * session will be called when the session is released.
- *
- *
- * @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)
- */
- public void releaseSession(@NonNull RoutingSessionInfo sessionInfo) {
- Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+ private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route) {
+ if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
+ Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
+ notifyTransferFailed(oldSession, route);
+ return;
+ }
+
+ int requestId = createTransferRequest(oldSession, route);
Client client = getOrCreateClient();
if (client != null) {
try {
- int requestId = mNextRequestId.getAndIncrement();
- mMediaRouterService.releaseSessionWithManager(
- client, requestId, sessionInfo.getId());
+ mMediaRouterService.requestCreateSessionWithManager(
+ client, requestId, oldSession, route);
} catch (RemoteException ex) {
- Log.e(TAG, "releaseSession: Failed to send a request", ex);
+ Log.e(TAG, "requestCreateSession: Failed to send a request", ex);
}
}
}
+ private int createTransferRequest(RoutingSessionInfo session, MediaRoute2Info route) {
+ int requestId = mNextRequestId.getAndIncrement();
+ TransferRequest transferRequest = new TransferRequest(requestId, session, route);
+ mTransferRequests.add(transferRequest);
+
+ Message timeoutMessage =
+ obtainMessage(MediaRouter2Manager::handleTransferTimeout, this, transferRequest);
+ mHandler.sendMessageDelayed(timeoutMessage, TRANSFER_TIMEOUT_MS);
+ return requestId;
+ }
+
+ private void handleTransferTimeout(TransferRequest request) {
+ boolean removed = mTransferRequests.remove(request);
+ if (removed) {
+ notifyTransferFailed(request.mOldSessionInfo, request.mTargetRoute);
+ }
+ }
+
+
private boolean areSessionsMatched(MediaController mediaController,
RoutingSessionInfo sessionInfo) {
MediaController.PlaybackInfo playbackInfo = mediaController.getPlaybackInfo();
@@ -849,9 +871,16 @@ public final class MediaRouter2Manager {
/**
* Called when a session is changed.
- * @param sessionInfo the updated session
+ * @param session the updated session
+ */
+ public void onSessionUpdated(@NonNull RoutingSessionInfo session) {}
+
+ /**
+ * Called when a session is released.
+ * @param session the released session.
+ * @see #releaseSession(RoutingSessionInfo)
*/
- public void onSessionUpdated(@NonNull RoutingSessionInfo sessionInfo) {}
+ public void onSessionReleased(@NonNull RoutingSessionInfo session) {}
/**
* Called when media is transferred.
@@ -907,7 +936,7 @@ public final class MediaRouter2Manager {
if (!(obj instanceof CallbackRecord)) {
return false;
}
- return mCallback == ((CallbackRecord) obj).mCallback;
+ return mCallback == ((CallbackRecord) obj).mCallback;
}
@Override
@@ -931,15 +960,21 @@ public final class MediaRouter2Manager {
class Client extends IMediaRouter2Manager.Stub {
@Override
- public void notifySessionCreated(int requestId, RoutingSessionInfo sessionInfo) {
+ public void notifySessionCreated(int requestId, RoutingSessionInfo session) {
mHandler.sendMessage(obtainMessage(MediaRouter2Manager::createSessionOnHandler,
- MediaRouter2Manager.this, requestId, sessionInfo));
+ MediaRouter2Manager.this, requestId, session));
+ }
+
+ @Override
+ public void notifySessionUpdated(RoutingSessionInfo session) {
+ mHandler.sendMessage(obtainMessage(MediaRouter2Manager::handleSessionsUpdatedOnHandler,
+ MediaRouter2Manager.this, session));
}
@Override
- public void notifySessionUpdated(RoutingSessionInfo sessionInfo) {
- mHandler.sendMessage(obtainMessage(MediaRouter2Manager::handleSessionsUpdated,
- MediaRouter2Manager.this, sessionInfo));
+ public void notifySessionReleased(RoutingSessionInfo session) {
+ mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifySessionReleased,
+ MediaRouter2Manager.this, session));
}
@Override
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index edf1fc58ecf56594eedf667857e52430de83785c..a5d25e0771fd9477e3911a60e1d132392dc18e2a 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -220,7 +220,7 @@ public final class RoutingSessionInfo implements Parcelable {
}
/**
- * Gets information about how volume is handled on the session.
+ * Gets the information about how volume is handled on the session.
*
* @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
* {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}.
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 26e65dda5ebf9ba20a9b4fcc938e83a3b3002c26..1bf2863989a57b84cb1727753aecda0cffe412d1 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -16,28 +16,22 @@
package android.media;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.lang.ref.WeakReference;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityThread;
-import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
-import android.media.PlayerBase;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.AndroidRuntimeException;
import android.util.Log;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.atomic.AtomicReference;
+
/**
* The SoundPool class manages and plays audio resources for applications.
@@ -122,13 +116,12 @@ public class SoundPool extends PlayerBase {
private final static String TAG = "SoundPool";
private final static boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final AtomicReference mEventHandler = new AtomicReference<>(null);
+
private long mNativeContext; // accessed by native methods
- private EventHandler mEventHandler;
- private SoundPool.OnLoadCompleteListener mOnLoadCompleteListener;
private boolean mHasAppOpsPlayAudio;
- private final Object mLock;
private final AudioAttributes mAttributes;
/**
@@ -159,7 +152,6 @@ public class SoundPool extends PlayerBase {
if (native_setup(new WeakReference(this), maxStreams, attributes) != 0) {
throw new RuntimeException("Native setup failed");
}
- mLock = new Object();
mAttributes = attributes;
baseRegisterPlayer();
@@ -491,21 +483,18 @@ public class SoundPool extends PlayerBase {
* Sets the callback hook for the OnLoadCompleteListener.
*/
public void setOnLoadCompleteListener(OnLoadCompleteListener listener) {
- synchronized(mLock) {
- if (listener != null) {
- // setup message handler
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mEventHandler = new EventHandler(looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mEventHandler = new EventHandler(looper);
- } else {
- mEventHandler = null;
- }
- } else {
- mEventHandler = null;
- }
- mOnLoadCompleteListener = listener;
+ if (listener == null) {
+ mEventHandler.set(null);
+ return;
+ }
+
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mEventHandler.set(new EventHandler(looper, listener));
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mEventHandler.set(new EventHandler(looper, listener));
+ } else {
+ mEventHandler.set(null);
}
}
@@ -525,35 +514,36 @@ public class SoundPool extends PlayerBase {
@SuppressWarnings("unchecked")
private static void postEventFromNative(Object ref, int msg, int arg1, int arg2, Object obj) {
SoundPool soundPool = ((WeakReference) ref).get();
- if (soundPool == null)
+ if (soundPool == null) {
return;
+ }
- if (soundPool.mEventHandler != null) {
- Message m = soundPool.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
- soundPool.mEventHandler.sendMessage(m);
+ Handler eventHandler = soundPool.mEventHandler.get();
+ if (eventHandler == null) {
+ return;
}
+
+ Message message = eventHandler.obtainMessage(msg, arg1, arg2, obj);
+ eventHandler.sendMessage(message);
}
private final class EventHandler extends Handler {
- public EventHandler(Looper looper) {
+ private final OnLoadCompleteListener mOnLoadCompleteListener;
+
+ EventHandler(Looper looper, @NonNull OnLoadCompleteListener onLoadCompleteListener) {
super(looper);
+ mOnLoadCompleteListener = onLoadCompleteListener;
}
@Override
public void handleMessage(Message msg) {
- switch(msg.what) {
- case SAMPLE_LOADED:
- if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
- synchronized(mLock) {
- if (mOnLoadCompleteListener != null) {
- mOnLoadCompleteListener.onLoadComplete(SoundPool.this, msg.arg1, msg.arg2);
- }
- }
- break;
- default:
+ if (msg.what != SAMPLE_LOADED) {
Log.e(TAG, "Unknown message type " + msg.what);
return;
}
+
+ if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
+ mOnLoadCompleteListener.onLoadComplete(SoundPool.this, msg.arg1, msg.arg2);
}
}
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index c4d27eca02f8799e5895d31d46088115065a6cb7..e719b2a047207b24bef3cf4080e2fa59a43e437c 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -90,6 +90,17 @@ public final class MediaProjectionManager {
* projection is stopped. This allows for user controls to be displayed on top of the screen
* being captured.
*
+ *
+ * Apps targeting SDK version {@link android.os.Build.VERSION_CODES#Q} or later should specify
+ * the foreground service type using the attribute {@link android.R.attr#foregroundServiceType}
+ * in the service element of the app's manifest file.
+ * The {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION} attribute
+ * should be specified.
+ *
+ *
+ * @see
+ * Foregroud Service Types
+ *
* @param resultCode The result code from {@link android.app.Activity#onActivityResult(int,
* int, android.content.Intent)}
* @param resultData The resulting data from {@link android.app.Activity#onActivityResult(int,
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl b/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl
index de4d060ce484919c3df0648c27145537fbf9c2ac..a237ec1aa3b31c2d77d3538f5ad45c8521379f39 100644
--- a/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl
+++ b/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl
@@ -43,9 +43,9 @@ parcelable RecognitionEvent {
boolean triggerInData;
/**
* Audio format of either the trigger in event data or to use for capture of the rest of the
- * utterance.
+ * utterance. May be null when no audio is available for this event type.
*/
- AudioConfig audioConfig;
+ @nullable AudioConfig audioConfig;
/** Additional data. */
byte[] data;
}
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 8ae98ae5e937b797751ab3908c5c1179aa680e66..23fadac8a72b9a301d7fed2183c760f6d31b3227 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -146,6 +146,8 @@ public class TvRecordingClient {
mPendingAppPrivateCommands.clear();
if (mSession != null) {
mSession.release();
+ mIsTuned = false;
+ mIsRecordingStarted = false;
mSession = null;
}
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index 68071b0b0fe3ce87a8b4644d2460cd0c61d2b593..bb00bb3b8d566fe2e24b8a958ac2503db7c4b207 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -20,12 +20,16 @@ import android.annotation.BytesLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.app.ActivityManager;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -72,9 +76,15 @@ public class DvrPlayback implements AutoCloseable {
*/
public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
+ private static final String TAG = "TvTunerPlayback";
+
private long mNativeContext;
private OnPlaybackStatusChangedListener mListener;
private Executor mExecutor;
+ private int mUserId;
+ private static int sInstantId = 0;
+ private int mSegmentId = 0;
+ private int mUnderflow;
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
@@ -88,6 +98,9 @@ public class DvrPlayback implements AutoCloseable {
private native long nativeRead(byte[] bytes, long offset, long size);
private DvrPlayback() {
+ mUserId = ActivityManager.getCurrentUser();
+ mSegmentId = (sInstantId & 0x0000ffff) << 16;
+ sInstantId++;
}
/** @hide */
@@ -98,6 +111,9 @@ public class DvrPlayback implements AutoCloseable {
}
private void onPlaybackStatusChanged(int status) {
+ if (status == PLAYBACK_STATUS_EMPTY) {
+ mUnderflow++;
+ }
if (mExecutor != null && mListener != null) {
mExecutor.execute(() -> mListener.onPlaybackStatusChanged(status));
}
@@ -154,6 +170,13 @@ public class DvrPlayback implements AutoCloseable {
*/
@Result
public int start() {
+ mSegmentId = (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff);
+ mUnderflow = 0;
+ Log.d(TAG, "Write Stats Log for Playback.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0);
return nativeStartDvr();
}
@@ -167,6 +190,11 @@ public class DvrPlayback implements AutoCloseable {
*/
@Result
public int stop() {
+ Log.d(TAG, "Write Stats Log for Playback.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mUnderflow);
return nativeStopDvr();
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index 198bd0f4e78e4a1446b4b29b44d2929d4d5f7e49..88711672596168f4f85569fb555dcc9031b70091 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -19,14 +19,19 @@ package android.media.tv.tuner.dvr;
import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.app.ActivityManager;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.internal.util.FrameworkStatsLog;
import java.util.concurrent.Executor;
+
/**
* Digital Video Record (DVR) recorder class which provides record control on Demux's output buffer.
*
@@ -34,9 +39,14 @@ import java.util.concurrent.Executor;
*/
@SystemApi
public class DvrRecorder implements AutoCloseable {
+ private static final String TAG = "TvTunerRecord";
private long mNativeContext;
private OnRecordStatusChangedListener mListener;
private Executor mExecutor;
+ private int mUserId;
+ private static int sInstantId = 0;
+ private int mSegmentId = 0;
+ private int mOverflow;
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
@@ -50,6 +60,9 @@ public class DvrRecorder implements AutoCloseable {
private native long nativeWrite(byte[] bytes, long offset, long size);
private DvrRecorder() {
+ mUserId = ActivityManager.getCurrentUser();
+ mSegmentId = (sInstantId & 0x0000ffff) << 16;
+ sInstantId++;
}
/** @hide */
@@ -60,6 +73,9 @@ public class DvrRecorder implements AutoCloseable {
}
private void onRecordStatusChanged(int status) {
+ if (status == Filter.STATUS_OVERFLOW) {
+ mOverflow++;
+ }
if (mExecutor != null && mListener != null) {
mExecutor.execute(() -> mListener.onRecordStatusChanged(status));
}
@@ -112,6 +128,13 @@ public class DvrRecorder implements AutoCloseable {
*/
@Result
public int start() {
+ mSegmentId = (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff);
+ mOverflow = 0;
+ Log.d(TAG, "Write Stats Log for Record.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0);
return nativeStartDvr();
}
@@ -124,6 +147,11 @@ public class DvrRecorder implements AutoCloseable {
*/
@Result
public int stop() {
+ Log.d(TAG, "Write Stats Log for Playback.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mOverflow);
return nativeStopDvr();
}
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index af63070027a25083e8cfc1e36162a48d18d15d6a..57a04fd70a0bf6dae5854ec8f5b00b9244f16755 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -29,6 +29,7 @@ import android.media.MediaCodec.LinearBlock;
@SystemApi
public class MediaEvent extends FilterEvent {
private long mNativeContext;
+ private boolean mReleased = false;
private final Object mLock = new Object();
private native Long nativeGetAudioHandle();
@@ -181,7 +182,21 @@ public class MediaEvent extends FilterEvent {
*/
@Override
protected void finalize() {
- nativeFinalize();
- mNativeContext = 0;
+ release();
+ }
+
+ /**
+ * Releases the MediaEvent object.
+ * @hide
+ */
+ public void release() {
+ synchronized (mLock) {
+ if (mReleased) {
+ return;
+ }
+ nativeFinalize();
+ mNativeContext = 0;
+ mReleased = true;
+ }
}
}
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 4a9da62f2517846e751beaaeb6c728951eaf187b..936edb3fb005813b31d9bb529bc5dc3c618d5aef 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -86,6 +86,14 @@ public:
void setBufferHeight(int height) { mHeight = height; }
int getBufferHeight() { return mHeight; }
+ void queueAttachedFlag(bool isAttached) {
+ Mutex::Autolock l(mAttachedFlagQueueLock);
+ mAttachedFlagQueue.push_back(isAttached);
+ }
+ void dequeueAttachedFlag() {
+ Mutex::Autolock l(mAttachedFlagQueueLock);
+ mAttachedFlagQueue.pop_back();
+ }
private:
static JNIEnv* getJNIEnv(bool* needsDetach);
static void detachJNI();
@@ -136,6 +144,11 @@ private:
};
static BufferDetacher sBufferDetacher;
+
+ // Buffer queue guarantees both producer and consumer side buffer flows are
+ // in order. See b/19977520. As a result, we can use a queue here.
+ Mutex mAttachedFlagQueueLock;
+ std::deque mAttachedFlagQueue;
};
JNIImageWriterContext::BufferDetacher JNIImageWriterContext::sBufferDetacher;
@@ -265,11 +278,23 @@ void JNIImageWriterContext::onBufferReleased() {
ALOGV("%s: buffer released", __FUNCTION__);
bool needsDetach = false;
JNIEnv* env = getJNIEnv(&needsDetach);
+
+ bool bufferIsAttached = false;
+ {
+ Mutex::Autolock l(mAttachedFlagQueueLock);
+ if (!mAttachedFlagQueue.empty()) {
+ bufferIsAttached = mAttachedFlagQueue.front();
+ mAttachedFlagQueue.pop_front();
+ } else {
+ ALOGW("onBufferReleased called with no attached flag queued");
+ }
+ }
+
if (env != NULL) {
// Detach the buffer every time when a buffer consumption is done,
// need let this callback give a BufferItem, then only detach if it was attached to this
- // Writer. Do the detach unconditionally for opaque format now. see b/19977520
- if (mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ // Writer. see b/19977520
+ if (mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED || bufferIsAttached) {
sBufferDetacher.detach(mProducer);
}
@@ -622,10 +647,16 @@ static void ImageWriter_queueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, j
return;
}
- // Finally, queue input buffer
+ // Finally, queue input buffer.
+ //
+ // Because onBufferReleased may be called before queueBuffer() returns,
+ // queue the "attached" flag before calling queueBuffer. In case
+ // queueBuffer() fails, remove it from the queue.
+ ctx->queueAttachedFlag(false);
res = anw->queueBuffer(anw.get(), buffer, fenceFd);
if (res != OK) {
ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
+ ctx->dequeueAttachedFlag();
switch (res) {
case NO_INIT:
jniThrowException(env, "java/lang/IllegalStateException",
@@ -720,10 +751,16 @@ static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nat
}
// Step 3. Queue Image.
+ //
+ // Because onBufferReleased may be called before queueBuffer() returns,
+ // queue the "attached" flag before calling queueBuffer. In case
+ // queueBuffer() fails, remove it from the queue.
+ ctx->queueAttachedFlag(true);
res = anw->queueBuffer(anw.get(), buffer->mGraphicBuffer.get(), /*fenceFd*/
-1);
if (res != OK) {
ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
+ ctx->dequeueAttachedFlag();
switch (res) {
case NO_INIT:
jniThrowException(env, "java/lang/IllegalStateException",
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index a03b24cbbcf6502ff437a6a68610798fe2051cb2..0b0e162d4faf28539c9c038ec936e9cbb06365cd 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -233,10 +233,15 @@ void JMediaCodec::release() {
}
void JMediaCodec::releaseAsync() {
- if (mCodec != NULL) {
- mCodec->releaseAsync();
- }
- mInitStatus = NO_INIT;
+ std::call_once(mAsyncReleaseFlag, [this] {
+ if (mCodec != NULL) {
+ sp notify = new AMessage(kWhatAsyncReleaseComplete, this);
+ // Hold strong reference to this until async release is complete
+ notify->setObject("this", this);
+ mCodec->releaseAsync(notify);
+ }
+ mInitStatus = NO_INIT;
+ });
}
JMediaCodec::~JMediaCodec() {
@@ -1084,6 +1089,15 @@ void JMediaCodec::onMessageReceived(const sp &msg) {
handleFrameRenderedNotification(msg);
break;
}
+ case kWhatAsyncReleaseComplete:
+ {
+ if (mLooper != NULL) {
+ mLooper->unregisterHandler(id());
+ mLooper->stop();
+ mLooper.clear();
+ }
+ break;
+ }
default:
TRESPASS();
}
@@ -1096,7 +1110,7 @@ void JMediaCodec::onMessageReceived(const sp &msg) {
using namespace android;
static sp setMediaCodec(
- JNIEnv *env, jobject thiz, const sp &codec) {
+ JNIEnv *env, jobject thiz, const sp &codec, bool release = true) {
sp old = (JMediaCodec *)env->CallLongMethod(thiz, gFields.lockAndGetContextID);
if (codec != NULL) {
codec->incStrong(thiz);
@@ -1107,7 +1121,9 @@ static sp setMediaCodec(
* its message handler, doing release() from there will deadlock
* (as MediaCodec::release() post synchronous message to the same looper)
*/
- old->release();
+ if (release) {
+ old->release();
+ }
old->decStrong(thiz);
}
env->CallVoidMethod(thiz, gFields.setAndUnlockContextID, (jlong)codec.get());
@@ -1122,7 +1138,8 @@ static sp getMediaCodec(JNIEnv *env, jobject thiz) {
}
static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
- sp codec = getMediaCodec(env, thiz);
+ // Clear Java native reference.
+ sp codec = setMediaCodec(env, thiz, nullptr, false /* release */);
if (codec != NULL) {
codec->releaseAsync();
}
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 5c34341a86a15d67dba3b1ef36d0f6db2df14325..a58f9a74b563529ff511517608640f56aea60dcd 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -173,6 +173,7 @@ private:
enum {
kWhatCallbackNotify,
kWhatFrameRendered,
+ kWhatAsyncReleaseComplete,
};
jclass mClass;
@@ -185,6 +186,7 @@ private:
bool mGraphicOutput{false};
bool mHasCryptoOrDescrambler{false};
std::once_flag mReleaseFlag;
+ std::once_flag mAsyncReleaseFlag;
sp mCallbackNotification;
sp mOnFrameRenderedNotification;
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 7e721406a3001c7574206fd35aee1e4f3f3b4507..515d610109abb5934bf7e97fa16b494cd89fd6b2 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -314,8 +314,9 @@ MediaEvent::~MediaEvent() {
if (mIonHandle != NULL) {
delete mIonHandle;
}
- if (mC2Buffer != NULL) {
- mC2Buffer->unregisterOnDestroyNotify(&DestroyCallback, this);
+ std::shared_ptr pC2Buffer = mC2Buffer.lock();
+ if (pC2Buffer != NULL) {
+ pC2Buffer->unregisterOnDestroyNotify(&DestroyCallback, this);
}
}
@@ -334,21 +335,23 @@ jobject MediaEvent::getLinearBlock() {
if (mLinearBlockObj != NULL) {
return mLinearBlockObj;
}
- mIonHandle = new C2HandleIon(mAvHandle->data[0], mDataLength);
+ mIonHandle = new C2HandleIon(dup(mAvHandle->data[0]), mDataLength);
std::shared_ptr block = _C2BlockFactory::CreateLinearBlock(mIonHandle);
JNIEnv *env = AndroidRuntime::getJNIEnv();
std::unique_ptr context{new JMediaCodecLinearBlock};
context->mBlock = block;
- mC2Buffer = context->toC2Buffer(0, mDataLength);
+ std::shared_ptr pC2Buffer = context->toC2Buffer(0, mDataLength);
+ context->mBuffer = pC2Buffer;
+ mC2Buffer = pC2Buffer;
if (mAvHandle->numInts > 0) {
// use first int in the native_handle as the index
int index = mAvHandle->data[mAvHandle->numFds];
std::shared_ptr c2param = std::make_shared(index, mDataId);
std::shared_ptr info(std::static_pointer_cast(c2param));
- mC2Buffer->setInfo(info);
+ pC2Buffer->setInfo(info);
}
- mC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
+ pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
jobject linearBlock =
env->NewObject(
env->FindClass("android/media/MediaCodec$LinearBlock"),
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index c469a3ad8b76e6ca8046e9f40d004b5f38822cd9..83e9db7963636fce4e436ba6c0142021476ca3bc 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -130,7 +130,7 @@ struct MediaEvent : public RefBase {
jweak mMediaEventObj;
jweak mLinearBlockObj;
C2HandleIon* mIonHandle;
- std::shared_ptr mC2Buffer;
+ std::weak_ptr mC2Buffer;
};
struct Filter : public RefBase {
diff --git a/media/jni/soundpool/Sound.cpp b/media/jni/soundpool/Sound.cpp
index c3abdc22b19de1ac6bf0127af91db5364e87bfcf..f8b4bdb1f4d55ab7fe660877fc5a35dd81bb268e 100644
--- a/media/jni/soundpool/Sound.cpp
+++ b/media/jni/soundpool/Sound.cpp
@@ -31,7 +31,7 @@ constexpr size_t kDefaultHeapSize = 1024 * 1024; // 1MB (compatible with low m
Sound::Sound(int32_t soundID, int fd, int64_t offset, int64_t length)
: mSoundID(soundID)
- , mFd(fcntl(fd, F_DUPFD_CLOEXEC)) // like dup(fd) but closes on exec to prevent leaks.
+ , mFd(fcntl(fd, F_DUPFD_CLOEXEC, (int)0 /* arg */)) // dup(fd) + close on exec to prevent leaks.
, mOffset(offset)
, mLength(length)
{
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index c05c21cf2752f795280e7bb1f99fe2ce31277b9e..ddefe266d897b2ee0f9fa95d070cd4d294a444cc 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -17,6 +17,7 @@
package com.android.mediaroutertest;
import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_PLAYBACK;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
@@ -28,6 +29,7 @@ import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_I
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID2;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID4_TO_SELECT_AND_DESELECT;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID6_TO_BE_IGNORED;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_FIXED_VOLUME;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_VARIABLE_VOLUME;
@@ -51,6 +53,7 @@ import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
@@ -79,6 +82,8 @@ public class MediaRouter2ManagerTest {
private static final int TIMEOUT_MS = 5000;
private static final String TEST_KEY = "test_key";
private static final String TEST_VALUE = "test_value";
+ private static final String TEST_ID_UNKNOWN = "id_unknown";
+ private static final String TEST_NAME_UNKNOWN = "unknown";
private Context mContext;
private MediaRouter2Manager mManager;
@@ -92,7 +97,6 @@ public class MediaRouter2ManagerTest {
public static final List FEATURES_ALL = new ArrayList();
public static final List FEATURES_SPECIAL = new ArrayList();
- private static final List FEATURES_LIVE_AUDIO = new ArrayList<>();
static {
FEATURES_ALL.add(FEATURE_SAMPLE);
@@ -100,8 +104,6 @@ public class MediaRouter2ManagerTest {
FEATURES_ALL.add(FEATURE_LIVE_AUDIO);
FEATURES_SPECIAL.add(FEATURE_SPECIAL);
-
- FEATURES_LIVE_AUDIO.add(FEATURE_LIVE_AUDIO);
}
@Before
@@ -109,7 +111,7 @@ public class MediaRouter2ManagerTest {
mContext = InstrumentationRegistry.getTargetContext();
mManager = MediaRouter2Manager.getInstance(mContext);
mRouter2 = MediaRouter2.getInstance(mContext);
- //TODO: If we need to support thread pool executors, change this to thread pool executor.
+ // If we need to support thread pool executors, change this to thread pool executor.
mExecutor = Executors.newSingleThreadExecutor();
mPackageName = mContext.getPackageName();
}
@@ -124,6 +126,7 @@ public class MediaRouter2ManagerTest {
StubMediaRoute2ProviderService instance = StubMediaRoute2ProviderService.getInstance();
if (instance != null) {
instance.setProxy(null);
+ instance.setSpy(null);
}
}
@@ -253,7 +256,6 @@ public class MediaRouter2ManagerTest {
CountDownLatch latch = new CountDownLatch(1);
addManagerCallback(new MediaRouter2Manager.Callback());
- //TODO: remove this when it's not necessary.
addRouterCallback(new MediaRouter2.RouteCallback() {});
addTransferCallback(new MediaRouter2.TransferCallback() {
@Override
@@ -277,17 +279,19 @@ public class MediaRouter2ManagerTest {
}
@Test
- public void testGetRoutingControllers() throws Exception {
+ public void testGetRoutingSessions() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
Map routes = waitAndGetRoutesWithManager(FEATURES_ALL);
+ MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
+
addRouterCallback(new RouteCallback() {});
addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
public void onTransferred(RoutingSessionInfo oldSessionInfo,
RoutingSessionInfo newSessionInfo) {
if (TextUtils.equals(mPackageName, newSessionInfo.getClientPackageName())
- && newSessionInfo.getSelectedRoutes().contains(ROUTE_ID1)) {
+ && newSessionInfo.getSelectedRoutes().contains(routeToSelect.getId())) {
latch.countDown();
}
}
@@ -295,11 +299,10 @@ public class MediaRouter2ManagerTest {
assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
- mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
- latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ mManager.selectRoute(mPackageName, routeToSelect);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
List sessions = mManager.getRoutingSessions(mPackageName);
-
assertEquals(2, sessions.size());
RoutingSessionInfo sessionInfo = sessions.get(1);
@@ -310,6 +313,77 @@ public class MediaRouter2ManagerTest {
assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
}
+ @Test
+ public void testTransfer_unknownRoute_fail() throws Exception {
+ addRouterCallback(new RouteCallback() {});
+
+ CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
+ CountDownLatch onTransferFailedLatch = new CountDownLatch(1);
+
+ addManagerCallback(new MediaRouter2Manager.Callback() {
+ @Override
+ public void onTransferred(RoutingSessionInfo oldSessionInfo,
+ RoutingSessionInfo newSessionInfo) {
+ assertNotNull(newSessionInfo);
+ onSessionCreatedLatch.countDown();
+ }
+ @Override
+ public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) {
+ onTransferFailedLatch.countDown();
+ }
+ });
+
+ MediaRoute2Info unknownRoute =
+ new MediaRoute2Info.Builder(TEST_ID_UNKNOWN, TEST_NAME_UNKNOWN)
+ .addFeature(FEATURE_REMOTE_PLAYBACK)
+ .build();
+
+ mManager.transfer(mManager.getSystemRoutingSession(), unknownRoute);
+ assertFalse(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertTrue(onTransferFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testRouterRelease_managerGetRoutingSessions() throws Exception {
+ CountDownLatch transferLatch = new CountDownLatch(1);
+ CountDownLatch releaseLatch = new CountDownLatch(1);
+
+ Map routes = waitAndGetRoutesWithManager(FEATURES_ALL);
+ MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
+ assertNotNull(routeToSelect);
+
+ addRouterCallback(new RouteCallback() {});
+ addManagerCallback(new MediaRouter2Manager.Callback() {
+ @Override
+ public void onTransferred(RoutingSessionInfo oldSessionInfo,
+ RoutingSessionInfo newSessionInfo) {
+ if (TextUtils.equals(mPackageName, newSessionInfo.getClientPackageName())
+ && newSessionInfo.getSelectedRoutes().contains(routeToSelect.getId())) {
+ transferLatch.countDown();
+ }
+ }
+ @Override
+ public void onSessionReleased(RoutingSessionInfo session) {
+ releaseLatch.countDown();
+ }
+ });
+
+ assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
+ assertEquals(1, mRouter2.getControllers().size());
+
+ mManager.transfer(mManager.getRoutingSessions(mPackageName).get(0), routeToSelect);
+ assertTrue(transferLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertEquals(2, mManager.getRoutingSessions(mPackageName).size());
+ assertEquals(2, mRouter2.getControllers().size());
+ mRouter2.getControllers().get(1).release();
+
+ assertTrue(releaseLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertEquals(1, mRouter2.getControllers().size());
+ assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
+ }
+
/**
* Tests select, transfer, release of routes of a provider
*/
@@ -350,6 +424,108 @@ public class MediaRouter2ManagerTest {
route -> TextUtils.equals(route.getClientPackageName(), null));
}
+ @Test
+ @LargeTest
+ public void testTransferTwice() throws Exception {
+ Map routes = waitAndGetRoutesWithManager(FEATURES_ALL);
+ addRouterCallback(new RouteCallback() { });
+
+ CountDownLatch successLatch1 = new CountDownLatch(1);
+ CountDownLatch successLatch2 = new CountDownLatch(1);
+ CountDownLatch failureLatch = new CountDownLatch(1);
+ CountDownLatch managerOnSessionReleasedLatch = new CountDownLatch(1);
+ CountDownLatch serviceOnReleaseSessionLatch = new CountDownLatch(1);
+ List sessions = new ArrayList<>();
+
+ StubMediaRoute2ProviderService instance = StubMediaRoute2ProviderService.getInstance();
+ assertNotNull(instance);
+ instance.setSpy(new StubMediaRoute2ProviderService.Spy() {
+ @Override
+ public void onReleaseSession(long requestId, String sessionId) {
+ serviceOnReleaseSessionLatch.countDown();
+ }
+ });
+
+ addManagerCallback(new MediaRouter2Manager.Callback() {
+ @Override
+ public void onTransferred(RoutingSessionInfo oldSession,
+ RoutingSessionInfo newSession) {
+ sessions.add(newSession);
+ if (successLatch1.getCount() > 0) {
+ successLatch1.countDown();
+ } else {
+ successLatch2.countDown();
+ }
+ }
+
+ @Override
+ public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) {
+ failureLatch.countDown();
+ }
+
+ @Override
+ public void onSessionReleased(RoutingSessionInfo session) {
+ managerOnSessionReleasedLatch.countDown();
+ }
+ });
+
+ MediaRoute2Info route1 = routes.get(ROUTE_ID1);
+ MediaRoute2Info route2 = routes.get(ROUTE_ID2);
+ assertNotNull(route1);
+ assertNotNull(route2);
+
+ mManager.selectRoute(mPackageName, route1);
+ assertTrue(successLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ mManager.selectRoute(mPackageName, route2);
+ assertTrue(successLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ // onTransferFailed/onSessionReleased should not be called.
+ assertFalse(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertFalse(managerOnSessionReleasedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ assertEquals(2, sessions.size());
+ List activeSessionIds = mManager.getActiveSessions().stream()
+ .map(RoutingSessionInfo::getId)
+ .collect(Collectors.toList());
+ // The old session shouldn't appear on the active session list.
+ assertFalse(activeSessionIds.contains(sessions.get(0).getId()));
+ assertTrue(activeSessionIds.contains(sessions.get(1).getId()));
+
+ assertFalse(serviceOnReleaseSessionLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ mManager.releaseSession(sessions.get(0));
+ assertTrue(serviceOnReleaseSessionLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertFalse(managerOnSessionReleasedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ @LargeTest
+ public void testTransfer_ignored_fails() throws Exception {
+ Map routes = waitAndGetRoutesWithManager(FEATURES_ALL);
+ addRouterCallback(new RouteCallback() {});
+
+ CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
+ CountDownLatch onFailedLatch = new CountDownLatch(1);
+
+ addManagerCallback(new MediaRouter2Manager.Callback() {
+ @Override
+ public void onTransferred(RoutingSessionInfo oldSessionInfo,
+ RoutingSessionInfo newSessionInfo) {
+ onSessionCreatedLatch.countDown();
+ }
+ @Override
+ public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) {
+ onFailedLatch.countDown();
+ }
+ });
+
+ List sessions = mManager.getRoutingSessions(mPackageName);
+ RoutingSessionInfo targetSession = sessions.get(sessions.size() - 1);
+ mManager.transfer(targetSession, routes.get(ROUTE_ID6_TO_BE_IGNORED));
+
+ assertFalse(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertTrue(onFailedLatch.await(MediaRouter2Manager.TRANSFER_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS));
+ }
@Test
public void testSetSystemRouteVolume() throws Exception {
// ensure client
@@ -488,11 +664,16 @@ public class MediaRouter2ManagerTest {
final int failureReason = REASON_REJECTED;
final CountDownLatch onRequestFailedLatch = new CountDownLatch(1);
+ final CountDownLatch onRequestFailedSecondCallLatch = new CountDownLatch(1);
addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
public void onRequestFailed(int reason) {
if (reason == failureReason) {
- onRequestFailedLatch.countDown();
+ if (onRequestFailedLatch.getCount() > 0) {
+ onRequestFailedLatch.countDown();
+ } else {
+ onRequestFailedSecondCallLatch.countDown();
+ }
}
}
});
@@ -504,6 +685,11 @@ public class MediaRouter2ManagerTest {
final long validRequestId = requestIds.get(0);
instance.notifyRequestFailed(validRequestId, failureReason);
assertTrue(onRequestFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ // Test calling notifyRequestFailed() multiple times with the same valid requestId.
+ // onRequestFailed() shouldn't be called since the requestId has been already handled.
+ instance.notifyRequestFailed(validRequestId, failureReason);
+ assertFalse(onRequestFailedSecondCallLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
@@ -640,8 +826,10 @@ public class MediaRouter2ManagerTest {
mRouter2.registerRouteCallback(mExecutor, routeCallback,
new RouteDiscoveryPreference.Builder(routeFeatures, true).build());
try {
- addedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
- featuresLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (mManager.getAllRoutes().isEmpty()) {
+ addedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+ }
+ featuresLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS);
return createRouteMap(mManager.getAvailableRoutes(mPackageName));
} finally {
mRouter2.unregisterRouteCallback(routeCallback);
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
index 4e398f26366ab0363413465ec13060ab83934a98..a51e3714b6f7ee72da9225ff1d357b65a6937188 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
@@ -53,6 +53,9 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
public static final String ROUTE_ID5_TO_TRANSFER_TO = "route_id5_to_transfer_to";
public static final String ROUTE_NAME5 = "Sample Route 5 - Route to transfer to";
+ public static final String ROUTE_ID6_TO_BE_IGNORED = "route_id6_to_be_ignored";
+ public static final String ROUTE_NAME6 = "Sample Route 6 - Route to be ignored";
+
public static final String ROUTE_ID_SPECIAL_FEATURE = "route_special_feature";
public static final String ROUTE_NAME_SPECIAL_FEATURE = "Special Feature Route";
@@ -76,6 +79,7 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
@GuardedBy("sLock")
private static StubMediaRoute2ProviderService sInstance;
private Proxy mProxy;
+ private Spy mSpy;
private void initializeRoutes() {
MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1)
@@ -98,7 +102,10 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
ROUTE_ID5_TO_TRANSFER_TO, ROUTE_NAME5)
.addFeature(FEATURE_SAMPLE)
.build();
-
+ MediaRoute2Info route6 = new MediaRoute2Info.Builder(
+ ROUTE_ID6_TO_BE_IGNORED, ROUTE_NAME6)
+ .addFeature(FEATURE_SAMPLE)
+ .build();
MediaRoute2Info routeSpecial =
new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_FEATURE, ROUTE_NAME_SPECIAL_FEATURE)
.addFeature(FEATURE_SAMPLE)
@@ -121,6 +128,7 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
mRoutes.put(route3.getId(), route3);
mRoutes.put(route4.getId(), route4);
mRoutes.put(route5.getId(), route5);
+ mRoutes.put(route6.getId(), route6);
mRoutes.put(routeSpecial.getId(), routeSpecial);
mRoutes.put(fixedVolumeRoute.getId(), fixedVolumeRoute);
@@ -219,6 +227,10 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
notifyRequestFailed(requestId, REASON_UNKNOWN_ERROR);
return;
}
+ // Ignores the request intentionally for testing
+ if (TextUtils.equals(ROUTE_ID6_TO_BE_IGNORED, routeId)) {
+ return;
+ }
maybeDeselectRoute(routeId);
final String sessionId = String.valueOf(mNextSessionId);
@@ -245,6 +257,11 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
@Override
public void onReleaseSession(long requestId, String sessionId) {
+ Spy spy = mSpy;
+ if (spy != null) {
+ spy.onReleaseSession(requestId, sessionId);
+ }
+
RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
if (sessionInfo == null) {
return;
@@ -364,7 +381,21 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
mProxy = proxy;
}
+ public void setSpy(@Nullable Spy spy) {
+ mSpy = spy;
+ }
+
+ /**
+ * It overrides the original service
+ */
public static class Proxy {
public void onSetRouteVolume(String routeId, int volume, long requestId) {}
}
+
+ /**
+ * It gets notified but doesn't prevent the original methods to be called.
+ */
+ public static class Spy {
+ public void onReleaseSession(long requestId, String sessionId) {}
+ }
}
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index 05a2e92ca080ca1f759204e7edec87c81dc2ff4f..f3730f2756d2cf04621ec347cbbde1603e70078d 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -84,6 +84,7 @@
?audio/sp-midi smf
?audio/x-matroska mka
?audio/x-pn-realaudio ra
+?audio/x-mpeg mp3
?image/bmp bmp
?image/heic heic
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 2a8a39a1fe1aacac47d528f2de354dbcd5c347ad..32b33a75853592690f14d1d541c9f2726bfb5652 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -32,6 +32,7 @@ android_library {
"SystemUIPluginLib",
"SystemUISharedLib",
"SettingsLib",
+ "car-ui-lib",
"android.car.userlib",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
@@ -95,6 +96,7 @@ android_library {
"androidx.slice_slice-builders",
"androidx.arch.core_core-runtime",
"androidx.lifecycle_lifecycle-extensions",
+ "car-ui-lib",
"SystemUI-tags",
"SystemUI-proto",
"metrics-helper-lib",
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index a8c70989253e79ab6c6d5ebe5476fb84ecaaaf8d..94816f81a4c5fb57c4ee63fb0275ba4451272441 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -79,7 +79,7 @@
android:gravity="bottom"
android:orientation="vertical">
-
+ android:paddingStart="20dp">
diff --git a/packages/CarSystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
index ca4e76ee104b92b1fd521619d15c0c852b653507..a8f1157420230c7bd550876d8e9ef1e2acb52b5f 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -27,7 +27,7 @@
android:animateLayoutChanges="true"
android:orientation="vertical">
-
-
-
+ android:layout_weight="1"
+ android:layoutDirection="ltr">
+
+
+
+
+
-
+ app:layout_constraintTop_toTopOf="parent">
+
+
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index d20ab49a22e61c78830144f26ce9e42fb360a887..ab9426593535993b7f6aa211a21cf48eca086ce9 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -15,7 +15,6 @@
~ limitations under the License
-->
- #40ffffff#000000@*android:color/car_body1_light
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 4bf0fca445d10be9e11f4009285efec3f95f321c..cf967c02bec53202b6fb75d28d9214e0f1f1e7c9 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -118,4 +118,7 @@
com.android.systemui.car.window.SystemUIOverlayWindowManagercom.android.systemui.car.volume.VolumeUI
+
+
+ 5000
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 371bebdebc86a35da022bc1e2e04258dfa7c7682..e76373d4a4f7b5177165dc5b46957c70e685383d 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -37,13 +37,9 @@
@*android:color/car_grey_50
-
-
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index ab61b443df97644f003b7691bf8370be06c9c3f8..69766cc6c0d0a1fdaead0147c025cf8ac79e5873 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -103,6 +103,7 @@ public class CarKeyguardViewController extends OverlayViewController implements
private KeyguardBouncer mBouncer;
private OnKeyguardCancelClickedListener mKeyguardCancelClickedListener;
private boolean mShowing;
+ private boolean mIsOccluded;
@Inject
public CarKeyguardViewController(
@@ -218,6 +219,15 @@ public class CarKeyguardViewController extends OverlayViewController implements
}
}
+ @Override
+ public void setOccluded(boolean occluded, boolean animate) {
+ mIsOccluded = occluded;
+ getOverlayViewGlobalStateController().setOccluded(occluded);
+ if (!occluded) {
+ reset(/* hideBouncerWhenShowing= */ false);
+ }
+ }
+
@Override
public void onCancelClicked() {
if (mBouncer == null) return;
@@ -236,6 +246,12 @@ public class CarKeyguardViewController extends OverlayViewController implements
@Override
public void dismissAndCollapse() {
+ // If dismissing and collapsing Keyguard is requested (e.g. by a Keyguard-dismissing
+ // Activity) while Keyguard is occluded, unocclude Keyguard so the user can authenticate to
+ // dismiss Keyguard.
+ if (mIsOccluded) {
+ setOccluded(/* occluded= */ false, /* animate= */ false);
+ }
if (!mBouncer.isSecure()) {
hide(/* startTime= */ 0, /* fadeoutDuration= */ 0);
}
@@ -314,11 +330,6 @@ public class CarKeyguardViewController extends OverlayViewController implements
// no-op
}
- @Override
- public void setOccluded(boolean occluded, boolean animate) {
- // no-op
- }
-
@Override
public boolean shouldDisableWindowAnimationsForUnlock() {
return false;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
index 4c720abb4c7489d8f5233841465f469338a7d19d..37dfce4e16ce7bae843dc562d39ccaae68e5f45d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
@@ -16,10 +16,10 @@
package com.android.systemui.car.navigationbar;
-import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_GESTURES;
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -368,15 +368,13 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("TopCarNavigationBar");
- lp.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
- lp.setFitInsetsTypes(0);
lp.windowAnimations = 0;
lp.gravity = Gravity.TOP;
mWindowManager.addView(mTopNavigationBarWindow, lp);
@@ -390,14 +388,13 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("BottomCarNavigationBar");
- lp.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR, ITYPE_BOTTOM_GESTURES};
lp.windowAnimations = 0;
lp.gravity = Gravity.BOTTOM;
mWindowManager.addView(mBottomNavigationBarWindow, lp);
@@ -415,6 +412,8 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
leftlp.setTitle("LeftCarNavigationBar");
+ leftlp.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR};
+ leftlp.setFitInsetsTypes(0);
leftlp.windowAnimations = 0;
leftlp.gravity = Gravity.LEFT;
mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
@@ -432,6 +431,8 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
rightlp.setTitle("RightCarNavigationBar");
+ rightlp.providesInsetsTypes = new int[]{ITYPE_EXTRA_NAVIGATION_BAR};
+ rightlp.setFitInsetsTypes(0);
rightlp.windowAnimations = 0;
rightlp.gravity = Gravity.RIGHT;
mWindowManager.addView(mRightNavigationBarWindow, rightlp);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
index 20fc1bcd60132a2e73526d32dc7622a777231a9f..029d4c7fa2fb4e07807218088346a75a00edf8ac 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
@@ -16,10 +16,7 @@
package com.android.systemui.car.navigationbar;
-import static android.view.WindowInsets.Type.systemBars;
-
import android.content.Context;
-import android.graphics.Insets;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -74,34 +71,17 @@ public class CarNavigationBarView extends LinearLayout {
mDarkIconManager.setShouldLog(true);
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
}
- // needs to be clickable so that it will receive ACTION_MOVE events
+ // Needs to be clickable so that it will receive ACTION_MOVE events.
setClickable(true);
+ // Needs to not be focusable so rotary won't highlight the entire nav bar.
+ setFocusable(false);
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
- applyMargins(windowInsets.getInsets(systemBars()));
return windowInsets;
}
- private void applyMargins(Insets insets) {
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- if (child.getLayoutParams() instanceof LayoutParams) {
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (lp.rightMargin != insets.right || lp.leftMargin != insets.left
- || lp.topMargin != insets.top || lp.bottomMargin != insets.bottom) {
- lp.rightMargin = insets.right;
- lp.leftMargin = insets.left;
- lp.topMargin = insets.top;
- lp.bottomMargin = insets.bottom;
- child.requestLayout();
- }
- }
- }
- }
-
// Used to forward touch events even if the touch was initiated from a child component
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
index 5e113d6366a1456477d7da575e7bdf983c40fd85..e7e33a5439f9f326456c61c7502df8920b18f616 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
@@ -32,8 +32,8 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
import java.net.URISyntaxException;
@@ -53,8 +53,8 @@ public class CarNavigationButton extends LinearLayout {
private static final String EXTRA_BUTTON_PACKAGES = "packages";
private Context mContext;
- private AlphaOptimizedImageButton mIcon;
- private AlphaOptimizedImageButton mMoreIcon;
+ private AlphaOptimizedImageView mIcon;
+ private AlphaOptimizedImageView mMoreIcon;
private ImageView mUnseenIcon;
private String mIntent;
private String mLongIntent;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
index 3b7b48a7718657f6952b0bd5122eff99cb43ebc8..d60bc418ece20343925aa18b8f8eefc78e2b5afe 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
@@ -24,6 +24,7 @@ import android.view.ViewGroup;
import androidx.annotation.LayoutRes;
+import com.android.car.ui.FocusParkingView;
import com.android.systemui.R;
import javax.inject.Inject;
@@ -146,6 +147,12 @@ public class NavigationBarViewFactory {
CarNavigationBarView view = (CarNavigationBarView) View.inflate(mContext, barLayout,
/* root= */ null);
+
+ // Include a FocusParkingView at the end. The rotary controller "parks" the focus here when
+ // the user navigates to another window. This is also used to prevent wrap-around which is
+ // why it must be first or last in Tab order.
+ view.addView(new FocusParkingView(mContext));
+
mCachedViewMap.put(type, view);
return mCachedViewMap.get(type);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java
index 6d140cae5442e21176910f12159d12adfa3c68bc..7d353f5acd9a239ff7dd030a35e613161c046702 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java
@@ -16,6 +16,7 @@
package com.android.systemui.car.notification;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayPanelViewController;
@@ -37,6 +38,7 @@ public class BottomNotificationPanelViewMediator extends NotificationPanelViewMe
NotificationPanelViewController notificationPanelViewController,
PowerManagerHelper powerManagerHelper,
+ BroadcastDispatcher broadcastDispatcher,
CarDeviceProvisionedController carDeviceProvisionedController,
ConfigurationController configurationController
@@ -44,6 +46,7 @@ public class BottomNotificationPanelViewMediator extends NotificationPanelViewMe
super(carNavigationBarController,
notificationPanelViewController,
powerManagerHelper,
+ broadcastDispatcher,
carDeviceProvisionedController,
configurationController);
notificationPanelViewController.setOverlayDirection(
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
index aeb1d39599db3c12064a7f7124a7c56279fca8b3..d4f720715a69eb562a3f6b17536e1c9381f73e5e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
@@ -24,7 +24,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.widget.FrameLayout;
import com.android.car.notification.R;
import com.android.car.notification.headsup.CarHeadsUpNotificationContainer;
@@ -44,7 +43,7 @@ public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotifica
private final OverlayViewGlobalStateController mOverlayViewGlobalStateController;
private final ViewGroup mWindow;
- private final FrameLayout mHeadsUpContentFrame;
+ private final ViewGroup mHeadsUpContentFrame;
@Inject
CarHeadsUpNotificationSystemContainer(Context context,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 1738091d14c962d7b99e17ecc7b5d0a5b101c533..1eead62c042a343a9ba107f6b2d73a5fc1129e62 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -48,17 +48,21 @@ import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.car.window.OverlayPanelViewController;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
+import java.util.concurrent.Executor;
+
import javax.inject.Inject;
import javax.inject.Singleton;
/** View controller for the notification panel. */
@Singleton
-public class NotificationPanelViewController extends OverlayPanelViewController {
+public class NotificationPanelViewController extends OverlayPanelViewController
+ implements CommandQueue.Callbacks {
private static final boolean DEBUG = true;
private static final String TAG = "NotificationPanelViewController";
@@ -68,12 +72,14 @@ public class NotificationPanelViewController extends OverlayPanelViewController
private final CarServiceProvider mCarServiceProvider;
private final IStatusBarService mBarService;
private final CommandQueue mCommandQueue;
+ private final Executor mUiBgExecutor;
private final NotificationDataManager mNotificationDataManager;
private final CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper;
private final CarNotificationListener mCarNotificationListener;
private final NotificationClickHandlerFactory mNotificationClickHandlerFactory;
private final StatusBarStateController mStatusBarStateController;
private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
+ private final NotificationVisibilityLogger mNotificationVisibilityLogger;
private float mInitialBackgroundAlpha;
private float mBackgroundAlphaDiff;
@@ -98,6 +104,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController
@Main Resources resources,
OverlayViewGlobalStateController overlayViewGlobalStateController,
FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
+ @UiBackground Executor uiBgExecutor,
/* Other things */
CarServiceProvider carServiceProvider,
@@ -110,6 +117,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController
CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
CarNotificationListener carNotificationListener,
NotificationClickHandlerFactory notificationClickHandlerFactory,
+ NotificationVisibilityLogger notificationVisibilityLogger,
/* Things that need to be replaced */
StatusBarStateController statusBarStateController
@@ -121,12 +129,15 @@ public class NotificationPanelViewController extends OverlayPanelViewController
mCarServiceProvider = carServiceProvider;
mBarService = barService;
mCommandQueue = commandQueue;
+ mUiBgExecutor = uiBgExecutor;
mNotificationDataManager = notificationDataManager;
mCarUxRestrictionManagerWrapper = carUxRestrictionManagerWrapper;
mCarNotificationListener = carNotificationListener;
mNotificationClickHandlerFactory = notificationClickHandlerFactory;
mStatusBarStateController = statusBarStateController;
+ mNotificationVisibilityLogger = notificationVisibilityLogger;
+ mCommandQueue.addCallback(this);
// Notification background setup.
mInitialBackgroundAlpha = (float) mResources.getInteger(
R.integer.config_initialNotificationBackgroundAlpha) / 100;
@@ -151,11 +162,35 @@ public class NotificationPanelViewController extends OverlayPanelViewController
.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
}
+ // CommandQueue.Callbacks
+
+ @Override
+ public void animateExpandNotificationsPanel() {
+ if (!isPanelExpanded()) {
+ toggle();
+ }
+ }
+
+ @Override
+ public void animateCollapsePanels(int flags, boolean force) {
+ if (isPanelExpanded()) {
+ toggle();
+ }
+ }
+
+ // OverlayViewController
+
@Override
protected void onFinishInflate() {
reinflate();
}
+ @Override
+ protected void hideInternal() {
+ super.hideInternal();
+ mNotificationVisibilityLogger.stop();
+ }
+
@Override
protected boolean shouldShowNavigationBar() {
return true;
@@ -197,6 +232,11 @@ public class NotificationPanelViewController extends OverlayPanelViewController
mUnseenCountUpdateListener.onUnseenCountUpdate(
mNotificationDataManager.getUnseenNotificationCount());
}
+ mCarNotificationListener.setNotificationsShown(
+ mNotificationDataManager.getSeenNotifications());
+ // This logs both when the notification panel is expanded and when the notification
+ // panel is scrolled.
+ mNotificationVisibilityLogger.log(isPanelExpanded());
});
mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
@@ -332,6 +372,8 @@ public class NotificationPanelViewController extends OverlayPanelViewController
mNotificationDataManager.clearAll();
}
+ // OverlayPanelViewController
+
@Override
protected boolean shouldAnimateCollapsePanel() {
return true;
@@ -363,6 +405,30 @@ public class NotificationPanelViewController extends OverlayPanelViewController
mNotificationView.setVisibleNotificationsAsSeen();
}
+ @Override
+ protected void onPanelVisible(boolean visible) {
+ super.onPanelVisible(visible);
+ mUiBgExecutor.execute(() -> {
+ try {
+ if (visible) {
+ // When notification panel is open even just a bit, we want to clear
+ // notification effects.
+ boolean clearNotificationEffects =
+ mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
+ mBarService.onPanelRevealed(clearNotificationEffects,
+ mNotificationDataManager.getVisibleNotifications().size());
+ } else {
+ mBarService.onPanelHidden();
+ }
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ Log.e(TAG, String.format(
+ "Unable to notify StatusBarService of panel visibility: %s", visible));
+ }
+ });
+
+ }
+
@Override
protected void onPanelExpanded(boolean expand) {
super.onPanelExpanded(expand);
@@ -373,6 +439,9 @@ public class NotificationPanelViewController extends OverlayPanelViewController
}
clearNotificationEffects();
}
+ if (!expand) {
+ mNotificationVisibilityLogger.log(isPanelExpanded());
+ }
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
index 41349b284147cd9168f188f72857eb8e068e9a3e..0c185bae819983f9a8952e655a81170b8704dc41 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
@@ -17,10 +17,17 @@
package com.android.systemui.car.notification;
import android.car.hardware.power.CarPowerManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
+import android.os.UserHandle;
+import android.util.Log;
import androidx.annotation.CallSuper;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayViewMediator;
@@ -37,18 +44,36 @@ import javax.inject.Singleton;
public class NotificationPanelViewMediator implements OverlayViewMediator,
ConfigurationController.ConfigurationListener {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "NotificationPanelVM";
+
private final CarNavigationBarController mCarNavigationBarController;
private final NotificationPanelViewController mNotificationPanelViewController;
private final PowerManagerHelper mPowerManagerHelper;
+ private final BroadcastDispatcher mBroadcastDispatcher;
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final ConfigurationController mConfigurationController;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) Log.v(TAG, "onReceive: " + intent);
+ String action = intent.getAction();
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ if (mNotificationPanelViewController.isPanelExpanded()) {
+ mNotificationPanelViewController.toggle();
+ }
+ }
+ }
+ };
+
@Inject
public NotificationPanelViewMediator(
CarNavigationBarController carNavigationBarController,
NotificationPanelViewController notificationPanelViewController,
PowerManagerHelper powerManagerHelper,
+ BroadcastDispatcher broadcastDispatcher,
CarDeviceProvisionedController carDeviceProvisionedController,
ConfigurationController configurationController
@@ -56,6 +81,7 @@ public class NotificationPanelViewMediator implements OverlayViewMediator,
mCarNavigationBarController = carNavigationBarController;
mNotificationPanelViewController = notificationPanelViewController;
mPowerManagerHelper = powerManagerHelper;
+ mBroadcastDispatcher = broadcastDispatcher;
mCarDeviceProvisionedController = carDeviceProvisionedController;
mConfigurationController = configurationController;
}
@@ -84,6 +110,9 @@ public class NotificationPanelViewMediator implements OverlayViewMediator,
return mNotificationPanelViewController.isPanelExpanded();
}
});
+
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), null, UserHandle.ALL);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationVisibilityLogger.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationVisibilityLogger.java
new file mode 100644
index 0000000000000000000000000000000000000000..44c819711bd2515045ed410ae10337aadb1a86ea
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationVisibilityLogger.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.notification;
+
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.car.notification.AlertEntry;
+import com.android.car.notification.NotificationDataManager;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.dagger.qualifiers.UiBackground;
+
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Handles notification logging, in particular, logging which notifications are visible and which
+ * are not.
+ */
+@Singleton
+public class NotificationVisibilityLogger {
+
+ private static final String TAG = "NotificationVisibilityLogger";
+
+ private final ArraySet mCurrentlyVisible = new ArraySet<>();
+ private final ArraySet mNewlyVisible = new ArraySet<>();
+ private final ArraySet mPreviouslyVisible = new ArraySet<>();
+ private final ArraySet mTmpCurrentlyVisible = new ArraySet<>();
+
+ private final IStatusBarService mBarService;
+ private final Executor mUiBgExecutor;
+ private final NotificationDataManager mNotificationDataManager;
+
+ private boolean mIsVisible;
+
+ private final Runnable mVisibilityReporter = new Runnable() {
+
+ @Override
+ public void run() {
+ if (mIsVisible) {
+ int count = mNotificationDataManager.getVisibleNotifications().size();
+ for (AlertEntry alertEntry : mNotificationDataManager.getVisibleNotifications()) {
+ NotificationVisibility visObj = NotificationVisibility.obtain(
+ alertEntry.getKey(),
+ /* rank= */ -1,
+ count,
+ mIsVisible,
+ NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA);
+ mTmpCurrentlyVisible.add(visObj);
+ if (!mCurrentlyVisible.contains(visObj)) {
+ mNewlyVisible.add(visObj);
+ }
+ }
+ }
+ mPreviouslyVisible.addAll(mCurrentlyVisible);
+ mPreviouslyVisible.removeAll(mTmpCurrentlyVisible);
+ onNotificationVisibilityChanged(mNewlyVisible, mPreviouslyVisible);
+
+ recycleAllVisibilityObjects(mCurrentlyVisible);
+ mCurrentlyVisible.addAll(mTmpCurrentlyVisible);
+
+ recycleAllVisibilityObjects(mPreviouslyVisible);
+ recycleAllVisibilityObjects(mNewlyVisible);
+ recycleAllVisibilityObjects(mTmpCurrentlyVisible);
+ }
+ };
+
+ @Inject
+ public NotificationVisibilityLogger(
+ @UiBackground Executor uiBgExecutor,
+ IStatusBarService barService,
+ NotificationDataManager notificationDataManager) {
+ mUiBgExecutor = uiBgExecutor;
+ mBarService = barService;
+ mNotificationDataManager = notificationDataManager;
+ }
+
+ /** Triggers a visibility report update to be sent to StatusBarService. */
+ public void log(boolean isVisible) {
+ mIsVisible = isVisible;
+ mUiBgExecutor.execute(mVisibilityReporter);
+ }
+
+ /** Stops logging, clearing all visibility objects. */
+ public void stop() {
+ recycleAllVisibilityObjects(mCurrentlyVisible);
+ }
+
+ /**
+ * Notify StatusBarService of change in notifications' visibility.
+ */
+ private void onNotificationVisibilityChanged(
+ Set newlyVisible, Set noLongerVisible) {
+ if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
+ return;
+ }
+
+ try {
+ mBarService.onNotificationVisibilityChanged(
+ cloneVisibilitiesAsArr(newlyVisible), cloneVisibilitiesAsArr(noLongerVisible));
+ } catch (RemoteException e) {
+ // Won't fail unless the world has ended.
+ Log.e(TAG, "Failed to notify StatusBarService of notification visibility change");
+ }
+ }
+
+ /**
+ * Clears array and recycles NotificationVisibility objects for reuse.
+ */
+ private static void recycleAllVisibilityObjects(ArraySet array) {
+ for (int i = 0; i < array.size(); i++) {
+ array.valueAt(i).recycle();
+ }
+ array.clear();
+ }
+
+ /**
+ * Converts Set of NotificationVisibility objects to primitive array.
+ */
+ private static NotificationVisibility[] cloneVisibilitiesAsArr(Set c) {
+ NotificationVisibility[] array = new NotificationVisibility[c.size()];
+ int i = 0;
+ for (NotificationVisibility nv : c) {
+ if (nv != null) {
+ array[i] = nv.clone();
+ }
+ i++;
+ }
+ return array;
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
index 8d3eb4c2bbeedd0fd4a11d4cfedcd83e44e3b550..89c9931ac76e58b8e22df7d87fe1c8f6f2567f34 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
@@ -16,6 +16,7 @@
package com.android.systemui.car.notification;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayPanelViewController;
@@ -37,6 +38,7 @@ public class TopNotificationPanelViewMediator extends NotificationPanelViewMedia
NotificationPanelViewController notificationPanelViewController,
PowerManagerHelper powerManagerHelper,
+ BroadcastDispatcher broadcastDispatcher,
CarDeviceProvisionedController carDeviceProvisionedController,
ConfigurationController configurationController
@@ -44,6 +46,7 @@ public class TopNotificationPanelViewMediator extends NotificationPanelViewMedia
super(carNavigationBarController,
notificationPanelViewController,
powerManagerHelper,
+ broadcastDispatcher,
carDeviceProvisionedController,
configurationController);
notificationPanelViewController.setOverlayDirection(
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
index a4230032858e552fd0f2df55c60e3843b6a6fede..13f2b7ed45db227cc62591036112e518a72b6199 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
@@ -23,6 +23,7 @@ import android.view.WindowManager;
import com.android.systemui.car.window.SystemUIOverlayWindowController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -49,12 +50,14 @@ public class DummyNotificationShadeWindowController extends NotificationShadeWin
DozeParameters dozeParameters,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
+ KeyguardViewMediator keyguardViewMediator,
KeyguardBypassController keyguardBypassController,
SysuiColorExtractor colorExtractor,
DumpManager dumpManager,
SystemUIOverlayWindowController overlayWindowController) {
super(context, windowManager, activityManager, dozeParameters, statusBarStateController,
- configurationController, keyguardBypassController, colorExtractor, dumpManager);
+ configurationController, keyguardViewMediator, keyguardBypassController,
+ colorExtractor, dumpManager);
mOverlayWindowController = overlayWindowController;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 775ef8152ca28b9c6e39b4dc83c2005d4cf87ed9..45f3d342fb6e8e1a79c872a218da961eaa956c54 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -23,13 +23,17 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.util.Log;
+import android.view.IWindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
import com.android.systemui.car.window.OverlayViewController;
@@ -44,13 +48,24 @@ import javax.inject.Singleton;
*/
@Singleton
public class UserSwitchTransitionViewController extends OverlayViewController {
- private static final String TAG = "UserSwitchTransitionViewController";
+ private static final String TAG = "UserSwitchTransition";
private static final String ENABLE_DEVELOPER_MESSAGE_TRUE = "true";
+ private static final boolean DEBUG = false;
private final Context mContext;
private final Handler mHandler;
private final Resources mResources;
private final UserManager mUserManager;
+ private final IWindowManager mWindowManagerService;
+ private final int mWindowShownTimeoutMs;
+ private final Runnable mWindowShownTimeoutCallback = () -> {
+ if (DEBUG) {
+ Log.w(TAG, "Window was not hidden within " + getWindowShownTimeoutMs() + " ms, so it"
+ + "was hidden by mWindowShownTimeoutCallback.");
+ }
+
+ handleHide();
+ };
@GuardedBy("this")
private boolean mShowing;
@@ -62,6 +77,7 @@ public class UserSwitchTransitionViewController extends OverlayViewController {
@Main Handler handler,
@Main Resources resources,
UserManager userManager,
+ IWindowManager windowManagerService,
OverlayViewGlobalStateController overlayViewGlobalStateController) {
super(R.id.user_switching_dialog_stub, overlayViewGlobalStateController);
@@ -70,6 +86,9 @@ public class UserSwitchTransitionViewController extends OverlayViewController {
mHandler = handler;
mResources = resources;
mUserManager = userManager;
+ mWindowManagerService = windowManagerService;
+ mWindowShownTimeoutMs = mResources.getInteger(
+ R.integer.config_userSwitchTransitionViewShownTimeoutMs);
}
/**
@@ -81,10 +100,20 @@ public class UserSwitchTransitionViewController extends OverlayViewController {
if (mPreviousUserId == newUserId || mShowing) return;
mShowing = true;
mHandler.post(() -> {
+ try {
+ mWindowManagerService.setSwitchingUser(true);
+ mWindowManagerService.lockNow(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to notify window manager service regarding user switch");
+ }
+
start();
populateDialog(mPreviousUserId, newUserId);
// next time a new user is selected, this current new user will be the previous user.
mPreviousUserId = newUserId;
+ // In case the window is still showing after WINDOW_SHOWN_TIMEOUT_MS, then hide the
+ // window and log a warning message.
+ mHandler.postDelayed(mWindowShownTimeoutCallback, mWindowShownTimeoutMs);
});
}
@@ -92,6 +121,12 @@ public class UserSwitchTransitionViewController extends OverlayViewController {
if (!mShowing) return;
mShowing = false;
mHandler.post(this::stop);
+ mHandler.removeCallbacks(mWindowShownTimeoutCallback);
+ }
+
+ @VisibleForTesting
+ int getWindowShownTimeoutMs() {
+ return mWindowShownTimeoutMs;
}
private void populateDialog(@UserIdInt int previousUserId, @UserIdInt int newUserId) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 30e26578bd73381c902e6d3830fc97b3daa0a53a..3969f92c690aadc33d04e1d9a1c5629705cd9d9e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -138,4 +138,11 @@ public class OverlayViewController {
protected boolean shouldShowNavigationBar() {
return false;
}
+
+ /**
+ * Returns {@code true} if this view should be hidden during the occluded state.
+ */
+ protected boolean shouldShowWhenOccluded() {
+ return false;
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 70260b0d4cef2b7bb4df050202126163964e8845..8e94109643131d8abe35110d06e4c1340c36a862 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -24,7 +24,9 @@ import androidx.annotation.VisibleForTesting;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -47,11 +49,16 @@ public class OverlayViewGlobalStateController {
private static final int UNKNOWN_Z_ORDER = -1;
private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
private final CarNavigationBarController mCarNavigationBarController;
+
+ private boolean mIsOccluded;
+
@VisibleForTesting
Map mZOrderMap;
@VisibleForTesting
SortedMap mZOrderVisibleSortedMap;
@VisibleForTesting
+ Set mViewsHiddenForOcclusion;
+ @VisibleForTesting
OverlayViewController mHighestZOrder;
@Inject
@@ -63,6 +70,7 @@ public class OverlayViewGlobalStateController {
mCarNavigationBarController = carNavigationBarController;
mZOrderMap = new HashMap<>();
mZOrderVisibleSortedMap = new TreeMap<>();
+ mViewsHiddenForOcclusion = new HashSet<>();
}
/**
@@ -91,6 +99,10 @@ public class OverlayViewGlobalStateController {
*/
public void showView(OverlayViewController viewController, @Nullable Runnable show) {
debugLog();
+ if (mIsOccluded && !viewController.shouldShowWhenOccluded()) {
+ mViewsHiddenForOcclusion.add(viewController);
+ return;
+ }
if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(true);
}
@@ -147,6 +159,10 @@ public class OverlayViewGlobalStateController {
*/
public void hideView(OverlayViewController viewController, @Nullable Runnable hide) {
debugLog();
+ if (mIsOccluded && mViewsHiddenForOcclusion.contains(viewController)) {
+ mViewsHiddenForOcclusion.remove(viewController);
+ return;
+ }
if (!viewController.isInflated()) {
Log.d(TAG, "Content cannot be hidden since it isn't inflated: "
+ viewController.getClass().getName());
@@ -240,6 +256,43 @@ public class OverlayViewGlobalStateController {
return mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowHUN();
}
+ /**
+ * Set the OverlayViewWindow to be in occluded or unoccluded state. When OverlayViewWindow is
+ * occluded, all views mounted to it that are not configured to be shown during occlusion will
+ * be hidden.
+ */
+ public void setOccluded(boolean occluded) {
+ if (occluded) {
+ // Hide views before setting mIsOccluded to true so the regular hideView logic is used,
+ // not the one used during occlusion.
+ hideViewsForOcclusion();
+ mIsOccluded = true;
+ } else {
+ mIsOccluded = false;
+ // show views after setting mIsOccluded to false so the regular showView logic is used,
+ // not the one used during occlusion.
+ showViewsHiddenForOcclusion();
+ }
+ }
+
+ private void hideViewsForOcclusion() {
+ HashSet viewsCurrentlyShowing = new HashSet<>(
+ mZOrderVisibleSortedMap.values());
+ viewsCurrentlyShowing.forEach(overlayController -> {
+ if (!overlayController.shouldShowWhenOccluded()) {
+ hideView(overlayController, overlayController::hideInternal);
+ mViewsHiddenForOcclusion.add(overlayController);
+ }
+ });
+ }
+
+ private void showViewsHiddenForOcclusion() {
+ mViewsHiddenForOcclusion.forEach(overlayViewController -> {
+ showView(overlayViewController, overlayViewController::showInternal);
+ });
+ mViewsHiddenForOcclusion.clear();
+ }
+
private void debugLog() {
if (!DEBUG) {
return;
@@ -250,5 +303,8 @@ public class OverlayViewGlobalStateController {
Log.d(TAG, "mZOrderVisibleSortedMap: " + mZOrderVisibleSortedMap);
Log.d(TAG, "mZOrderMap.size(): " + mZOrderMap.size());
Log.d(TAG, "mZOrderMap: " + mZOrderMap);
+ Log.d(TAG, "mIsOccluded: " + mIsOccluded);
+ Log.d(TAG, "mViewsHiddenForOcclusion: " + mViewsHiddenForOcclusion);
+ Log.d(TAG, "mViewsHiddenForOcclusion.size(): " + mViewsHiddenForOcclusion.size());
}
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
index 38836d85e8d47969d82e3a915a26bb0ce9c9abae..189e240169c3da83b6f0c0e7391684d30f81d866 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -168,6 +169,18 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase {
any());
}
+ @Test
+ public void setOccludedFalse_currentlyOccluded_bouncerReset() {
+ when(mBouncer.isSecure()).thenReturn(true);
+ mCarKeyguardViewController.show(/* options= */ null);
+ mCarKeyguardViewController.setOccluded(/* occluded= */ true, /* animate= */ false);
+ reset(mBouncer);
+
+ mCarKeyguardViewController.setOccluded(/* occluded= */ false, /* animate= */ false);
+
+ verify(mBouncer).show(/* resetSecuritySelection= */ true);
+ }
+
@Test
public void onCancelClicked_callsCancelClickedListener() {
when(mBouncer.isSecure()).thenReturn(true);
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
index 54282d39998b006dd125b03108a57ff8905e526a..bcaa5e9a03ee70f26eb9eb9da54e1867606c3a90 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
@@ -36,8 +36,8 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
-import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -74,7 +74,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
@Test
public void onCreate_iconIsVisible() {
- AlphaOptimizedImageButton icon = mDefaultButton.findViewById(
+ AlphaOptimizedImageView icon = mDefaultButton.findViewById(
R.id.car_nav_button_icon_image);
assertThat(icon.getDrawable()).isNotNull();
@@ -83,12 +83,12 @@ public class CarNavigationButtonTest extends SysuiTestCase {
@Test
public void onSelected_selectedIconDefined_togglesIcon() {
mDefaultButton.setSelected(true);
- Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable selectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
mDefaultButton.setSelected(false);
- Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable unselectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(selectedIconDrawable).isNotEqualTo(unselectedIconDrawable);
@@ -100,12 +100,12 @@ public class CarNavigationButtonTest extends SysuiTestCase {
R.id.selected_icon_undefined);
selectedIconUndefinedButton.setSelected(true);
- Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable selectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
selectedIconUndefinedButton.setSelected(false);
- Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable unselectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(selectedIconDrawable).isEqualTo(unselectedIconDrawable);
@@ -150,7 +150,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
@Test
public void onSelected_doesNotShowMoreWhenSelected_doesNotShowMoreIcon() {
mDefaultButton.setSelected(true);
- AlphaOptimizedImageButton moreIcon = mDefaultButton.findViewById(
+ AlphaOptimizedImageView moreIcon = mDefaultButton.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
@@ -161,7 +161,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
CarNavigationButton showMoreWhenSelected = mTestView.findViewById(
R.id.not_highlightable_more_button);
showMoreWhenSelected.setSelected(true);
- AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
+ AlphaOptimizedImageView moreIcon = showMoreWhenSelected.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.VISIBLE);
@@ -173,7 +173,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
R.id.highlightable_no_more_button);
showMoreWhenSelected.setSelected(true);
showMoreWhenSelected.setSelected(false);
- AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
+ AlphaOptimizedImageView moreIcon = showMoreWhenSelected.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
@@ -187,7 +187,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
roleBasedButton.setSelected(false);
roleBasedButton.setAppIcon(appIcon);
- Drawable currentDrawable = ((AlphaOptimizedImageButton) roleBasedButton.findViewById(
+ Drawable currentDrawable = ((AlphaOptimizedImageView) roleBasedButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(currentDrawable).isEqualTo(appIcon);
@@ -212,7 +212,7 @@ public class CarNavigationButtonTest extends SysuiTestCase {
roleBasedButton.setSelected(true);
roleBasedButton.setAppIcon(appIcon);
- Drawable currentDrawable = ((AlphaOptimizedImageButton) roleBasedButton.findViewById(
+ Drawable currentDrawable = ((AlphaOptimizedImageView) roleBasedButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(currentDrawable).isEqualTo(appIcon);
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..89dac58cd2a7324c52a5d87d43f82e1004e1e320
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/NotificationVisibilityLoggerTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.car.notification.AlertEntry;
+import com.android.car.notification.NotificationDataManager;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class NotificationVisibilityLoggerTest extends SysuiTestCase {
+
+ private static final String PKG = "package_1";
+ private static final String OP_PKG = "OpPackage";
+ private static final int ID = 1;
+ private static final String TAG = "Tag";
+ private static final int UID = 2;
+ private static final int INITIAL_PID = 3;
+ private static final String CHANNEL_ID = "CHANNEL_ID";
+ private static final String CONTENT_TITLE = "CONTENT_TITLE";
+ private static final String OVERRIDE_GROUP_KEY = "OVERRIDE_GROUP_KEY";
+ private static final long POST_TIME = 12345L;
+ private static final UserHandle USER_HANDLE = new UserHandle(12);
+
+ @Mock
+ private IStatusBarService mBarService;
+ @Mock
+ private NotificationDataManager mNotificationDataManager;
+
+ private NotificationVisibilityLogger mNotificationVisibilityLogger;
+ private FakeExecutor mUiBgExecutor;
+ private AlertEntry mMessageNotification;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(/* testClass= */this);
+
+ mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
+ Notification.Builder mNotificationBuilder1 = new Notification.Builder(mContext, CHANNEL_ID)
+ .setContentTitle(CONTENT_TITLE);
+ mMessageNotification = new AlertEntry(new StatusBarNotification(PKG, OP_PKG,
+ ID, TAG, UID, INITIAL_PID, mNotificationBuilder1.build(), USER_HANDLE,
+ OVERRIDE_GROUP_KEY, POST_TIME));
+
+ when(mNotificationDataManager.getVisibleNotifications()).thenReturn(
+ Collections.singletonList(mMessageNotification));
+
+ mNotificationVisibilityLogger = new NotificationVisibilityLogger(
+ mUiBgExecutor, mBarService, mNotificationDataManager);
+ }
+
+ @Test
+ public void log_notifiesStatusBarService() throws RemoteException {
+ mNotificationVisibilityLogger.log(/* isVisible= */ true);
+ mUiBgExecutor.runNextReady();
+
+ verify(mBarService).onNotificationVisibilityChanged(
+ any(NotificationVisibility[].class), any(NotificationVisibility[].class));
+ }
+
+ @Test
+ public void log_isVisibleIsTrue_notifiesOfNewlyVisibleItems() throws RemoteException {
+ ArgumentCaptor newlyVisibleCaptor =
+ ArgumentCaptor.forClass(NotificationVisibility[].class);
+ ArgumentCaptor previouslyVisibleCaptor =
+ ArgumentCaptor.forClass(NotificationVisibility[].class);
+
+ mNotificationVisibilityLogger.log(/* isVisible= */ true);
+ mUiBgExecutor.runNextReady();
+
+ verify(mBarService).onNotificationVisibilityChanged(
+ newlyVisibleCaptor.capture(), previouslyVisibleCaptor.capture());
+ assertThat(newlyVisibleCaptor.getValue().length).isEqualTo(1);
+ assertThat(previouslyVisibleCaptor.getValue().length).isEqualTo(0);
+ }
+
+ @Test
+ public void log_isVisibleIsFalse_notifiesOfPreviouslyVisibleItems() throws RemoteException {
+ ArgumentCaptor